Loading data...
SPY
S&P 500 ETF
$675.02
+$6.29
+0.94%
CHECKING
SPY - S&P 500 ETF | INTRADAY
📅 -- 🕐 --
Today: L: $-- | H: $--
52W: $-- - $--
Vol: -- vs Avg
⏱️ --

📊 Market Conditions Guard Rail

FAVORABLE
VIX Level
14.7
Low Vol ✅
SPY Trend
↑ UP
Above 20MA ✅
Breadth
56%
Positive ✅
Conditions
3/3
Green ✅
✅ Market conditions support trading. Proceed with normal positioning.

🎯 TODAY'S TOP TRADE

SWING TRADE
Updated: --
THE TRADE
BUY SPY CALL
⭐⭐⭐⭐ 78% Confidence
📋 WHAT TO BUY
SPY $690 CALL
Expires: Dec 20 (5 days)
⏰ WHEN TO BUY
Market Open
Entry: $1.50 - $2.00
💰 TAKE PROFIT
+50% ($3.00)
When SPY hits $695
🛑 STOP LOSS
-40% ($0.90)
If SPY drops to $685
Risk/Reward: 1:1.25
Max Risk: $60/contract
Max Gain: $75/contract
💵 PROFIT CALCULATOR
$
You need to buy:
2
contracts
Total Cost:
$72
Max Loss:
$29
Based on entry at $0.36, target at $0.54 (+50%), stop at $0.22 (-40%)
Price
--
Loading...
Change
--
--
Volume
--
vs avg
Signal
--
--
ATR
--
Volatility
📊 Fibonacci Price Zones
S3 (61.8%)
$--
S2 (50%)
$--
S1 (38.2%)
$--
📍 CURRENT
$--
R1 (100%)
$--
R2 (127.2%)
$--
R3 (161.8%)
$--
Current Zone: Between S1 and R1
📈 Multi-Timeframe Trend
⚡ INTRADAY
BULLISH
📈 SWING
BULLISH
🎯 POSITION
BULLISH
🔮 Gann Quick View
Trend Vol vs Avg Bars in Trend Cycle Phase Next Cycle Turn
BULLISH +12% 7 MID Dec 21
Gann Score Agent Consensus Reversal Risk MTF Alignment Entry Window
75/100 3/4 LOW 3/3 OPEN
📋 Reference Data
52W High$609.07
52W Low$479.05
Today High$607.51
Today Low$604.35
Avg Vol (20D)42.5M
Vol vs Avg+12%
Open$605.20
Prev Close$604.85
SPY - S&P 500 📊 Trade Statistics (Last 30 Days)
Win Rate
68%
Avg Win
+$3.42
Avg Loss
-$1.85
Total Trades
22
Best Day
+$8.50
Worst Day
-$4.20
Current Streak
3 wins
📅 Upcoming Market Events (Next 7 Days)
Dec 11 (Wed)
CPI Report 8:30 AM
Dec 12 (Thu)
PPI Report 8:30 AM
Dec 12 (Thu)
Jobless Claims
Dec 18 (Wed)
FOMC Decision 2:00 PM
Dec 13 (Fri)
Quad Witching
Dec 16 (Mon)
Retail Sales
📊 SPY Pattern Detection (Last 14 Days)
PATTERN TYPE DATE DETECTED TIMEFRAME CONFIDENCE TARGET IMPLICATION STATUS
📅 SPY Historical Data (Last 10 Days)
Date O/H/L/C Change GAP Candle Trend Close Pos Vol O→H Events
Dec 10 605.20/609.00/601.00/607.57 +$2.47 +$0.10 Bullish UP Upper +12% +$3.80 -
Dec 9 600.50/606.20/598.30/605.10 +$4.60 +$0.20 Bullish UP Upper +8% +$5.70 -
Dec 6 595.80/602.40/594.10/600.30 +$4.50 -$0.30 Bullish UP Upper -5% +$6.60 NFP
Dec 5 598.90/600.20/592.50/595.60 -$3.30 +$0.50 Bearish DN Lower +15% +$1.30 -
Dec 4 602.10/604.30/596.80/598.70 -$3.40 +$1.20 Bearish DN Lower +3% +$2.20 ISM
Dec 3 599.50/603.80/598.20/602.10 +$2.60 -$0.80 Bullish UP Upper -2% +$4.30 -
Dec 2 596.20/600.90/595.00/599.50 +$3.30 +$0.40 Bullish UP Mid +6% +$4.70 -
Nov 29 594.80/597.50/593.20/596.20 +$1.40 +$0.60 Doji FLAT Mid -45% +$2.70 ½ Day
Nov 27 592.50/595.80/591.20/594.80 +$2.30 +$0.30 Bullish UP Upper -10% +$3.30 -
Nov 26 590.80/593.90/589.50/592.50 +$1.70 -$0.20 Bullish UP Upper +2% +$3.10 -
🧠 AI Quick Analysis
Loading AI analysis...
📊 Data Pattern Detection
📈
Loading pattern detection...
🔢 Bar Count Analysis
Current Symbol: SPY (synced with header)
📊 Base Confluence
+5
Consecutive Bars
🔮 Gann-Elliott
+7
Wave Progress
🧠 DQN/RL
+2
Momentum
🌊 3-Wave
+4
Impulse
📊 Trend Reversal Statistics

Average bars before trend reversal based on historical data. Sorted by reversal risk (HIGH first).

SymbolAvg Bullish RunAvg Bearish RunCurrent Run% of AvgReversal Risk
XLV 9 bars 7 bars -8 (Bear) 114% HIGH
XLB 8 bars 10 bars -9 (Bear) 90% HIGH
XLY 11 bars 9 bars +10 (Bull) 91% HIGH
XLK 11 bars 8 bars +8 (Bull) 73% MEDIUM
XLE 7 bars 9 bars -6 (Bear) 67% MEDIUM
XLI 10 bars 8 bars +6 (Bull) 60% MEDIUM
SPY 12 bars 8 bars +7 (Bull) 58% LOW
QQQ 10 bars 7 bars +5 (Bull) 50% LOW
XLP 8 bars 7 bars +4 (Bull) 50% LOW
XLF 8 bars 6 bars +3 (Bull) 38% LOW
IWM 9 bars 11 bars -4 (Bear) 36% LOW
XLU 7 bars 6 bars +2 (Bull) 29% LOW
📈 Bar Count History
#DateO/H/L/CDirBaseGannDQNWaveAvg

📊 Historical Averages (Last 20 Days)

Use these to set realistic daily targets. Don't expect more than the average move.

SYMBOL AVG O→H MAX O→H AVG O→L MAX O→L AVG O→C AVG RANGE CALL TARGET PUT TARGET
SPY +$4.25 +$8.50 -$3.80 -$7.20 +$1.20 $8.05 +$3.50 -$3.00
QQQ +$5.10 +$10.20 -$4.50 -$9.80 +$0.85 $9.60 +$4.25 -$3.75
IWM +$2.40 +$5.10 -$2.20 -$4.80 +$0.45 $4.60 +$2.00 -$1.80
XLF +$0.45 +$0.95 -$0.40 -$0.85 +$0.12 $0.85 +$0.38 -$0.33
XLE +$0.85 +$1.80 -$0.75 -$1.65 +$0.08 $1.60 +$0.70 -$0.62
XLK +$2.80 +$5.60 -$2.50 -$5.20 +$0.65 $5.30 +$2.30 -$2.05
💡 How to use: CALL TARGET = 85% of AVG O→H (realistic profit). PUT TARGET = 85% of AVG O→L. Don't chase moves beyond MAX values - those are rare outliers.

📈 Today vs Average

Today O→H vs Avg
89%
$3.80 of $4.25 avg
Nearing avg - consider taking profits
Today O→L vs Avg
110%
$4.20 of $3.80 avg
Exceeded avg - strong selling pressure
Today Range vs Avg
99%
$8.00 of $8.05 avg
Normal volatility day
⚡ Intraday vs 🌊 Swing vs 📈 Position Parameters
Parameter⚡ INTRADAY🌊 SWING📈 POSITION
Timeframe5-min, 15-minDaily, 4-HourWeekly, Monthly
Options Expiry0DTE - 1DTE3-5 DTE30-45 DTE
Fast SMA51020
Slow SMA203050
Stop Loss0.5-1.0 ATR1.5-2.0 ATR2.5-3.0 ATR
Target1.0-2.0 ATR3.0-4.0 ATR5.0-8.0 ATR
🤖 Agent Signals
Agent Intraday Conf Swing Conf Position Conf
📊 Base Confluence Agent BULLISH 72% BULLISH 68% BULLISH 75%
🔮 Gann-Elliott Agent BULLISH 75% BULLISH 70% BULLISH 78%
🧠 DQN/RL Agent NEUTRAL 58% BULLISH 65% BULLISH 71%
🌊 3-Wave Agent BULLISH 68% BEARISH 62% BULLISH 74%
CONSENSUS 3/4 BULLISH 68% 3/4 BULLISH 66% 4/4 BULLISH 75%

📐 Fibonacci Levels ( SPY )

⚠️ SPY is 0.8% away from R1 ($692.50)
LEVEL PRICE DISTANCE 📍 DATE SET TF IMPLICATION
R3 (161.8%) $720.40 +4.8% - Nov 15 Weekly Strong Resistance - Major Target
R2 (127.2%) $705.20 +2.6% - Nov 15 Daily Resistance - Take Partial Profits
R1 (100%) $692.50 +0.8% ⚠️ NEAR Dec 1 Daily APPROACHING - First Target
CURRENT $687.57 -- 📍 HERE -- -- Current Price Level
S1 (38.2%) $678.20 -1.4% - Dec 1 Daily First Support - Bounce Zone
S2 (50%) $672.40 -2.2% - Nov 15 Daily Support - Consider Adding
S3 (61.8%) $665.80 -3.2% - Nov 15 Weekly Strong Support - Major Buy Zone

📊 Technical Patterns Detected

PATTERN TYPE DATE DETECTED TIMEFRAME CONFIDENCE TARGET IMPLICATION STATUS
Double Bottom Bullish Dec 8 Daily 72% $705.00 Reversal confirmed at support ACTIVE
Ascending Triangle Bullish Dec 6 4-Hour 65% $700.00 Breakout pending above $692 ACTIVE
Bullish Engulfing Bullish Dec 10 Daily 78% $698.50 Momentum shift confirmed FORMING
Inv Head & Shoulders Bullish Dec 3 Weekly 58% $720.00 Major reversal potential WATCH
Rising Wedge Bearish Dec 5 4-Hour 45% $675.00 Potential pullback setup WATCH
Bullish Patterns
5
Bearish Patterns
1
Avg Confidence
71%
Active Setups
6
📊 Technical
Double Bottom72%
Daily | Dec 8 | Reversal confirmed at support
Ascending Triangle65%
4-Hour | Dec 6 | Breakout pending above $600
🕯️ Candlestick
Bullish Engulfing78%
Daily | Dec 10 | Strong momentum shift
Hammer70%
4-Hour | Dec 9 | Support bounce confirmed
⬛ Point & Figure
Double Top BO75%
Daily | Dec 7 | Breakout target $605
Bullish Catapult68%
Weekly | Dec 5 | Strong continuation signal

📝 Trade Journal

Total Trades
0
0 this week
Win Rate
0%
0W / 0L
Total P&L
$0.00
Avg: $0
Best Trade
$0.00
--
Worst Trade
$0.00
--
📝 Log New Trade
📋 Trade History (0 trades)
# Date Symbol Type Strike Contracts Entry Exit P&L Consensus Status Notes Actions
No trades logged yet. Start by logging your first trade above!
📊 Performance by Symbol

Log trades to see performance breakdown

📈 Performance by Direction

Log trades to see CALL vs PUT performance

🎯 Agent Consensus Analysis
4/4 ULTRA
--%
0 trades
3/4 SUPER
--%
0 trades
2/4 SINGLE
--%
0 trades
1/4 WEAK
--%
0 trades

Trading Opportunities - All Symbols

Total Opportunities
12
Ready Now
2
Coming Soon
4
Calls / Puts
7 / 5
STATUS SYMBOL SECTOR PRICE NEXT TURN DAYS STAGE % DONE RISK TURN DIR TRADE TARGET PATTERN TF DETECTED SCORE ACTION
READYSPYIndex$594.82Dec 100LATE78%HIGH▲ RALLYCALL$612.50Cycle LowDailyDec 0872
READYQQQTech$518.34Dec 100MID55%MED▲ RALLYCALL$538.00Cycle LowDailyDec 0868
SOONIWMSmall Cap$255.30Dec 122EXHAUSTED92%HIGH▼ BREAKDOWNPUT$245.00Cycle HighDailyDec 0965
SOONXLFFinancial$54.40Dec 133EARLY25%LOW▲ PULLBACKCALL$56.00Cycle LowWeeklyDec 0771
SOONXLEEnergy$89.45Dec 144LATE85%HIGH▼ BREAKDOWNPUT$84.00Cycle HighDailyDec 0958
SOONXLKTechnology$234.78Dec 111MID48%LOW▲ RALLYCALL$245.00Cycle LowDailyDec 0874
WAITINGXLVHealthcare$146.32Dec 188EARLY18%LOW▲ PULLBACKCALL$152.00Cycle LowWeeklyDec 0552
WAITINGXLIIndustrial$139.87Dec 2010MID62%MED▲ RALLYCALL$148.00Cycle LowWeeklyDec 0448
WAITINGXLBMaterials$89.45Dec 2212LATE88%HIGH▼ BREAKDOWNPUT$84.00Cycle HighDailyDec 0645
WAITINGXLUUtilities$77.89Dec 2515EARLY12%LOW⟳ REVERSALCALL$82.00Cycle LowWeeklyDec 0341
WAITINGXLPConsumer Staples$81.23Dec 2818EARLY22%LOW▲ PULLBACKCALL$85.00Cycle LowDailyDec 0238
WAITINGXLYConsumer Disc$225.67Dec 3020MID45%LOW▲ RALLYCALL$240.00Cycle LowWeeklyDec 0135
🟢
System Healthy
All systems operational
🟢 4 🟡 0 🔴 0
📊
🟢
Data Freshness
Checking...
--
🏛️
🟢
Market Status
Checking...
--
⚙️
🟢
Agent Workflow
Checking...
--
🟢
Data Quality
Checking...
--
📅 Data Timeline
Last Data Point: --
Days Since Update: --
Expected Update: --
Total Bars Loaded: --
Date Range: --
🔌 Connection Status
ConnectionStatusLight
GitHub Pages -- 🟢
Data Feed (SPY.csv) -- 🟢
Portfolio Feed -- 🟢
Local Storage -- 🟢
⚠️ Alerts & Issues
ℹ️ Running initial health check...
⚙️ Component Status
ComponentLast RunStatus
Data Fetch -- 🟢
Agent Workflow -- 🟢
Health Check -- 🟢
GitHub Deploy -- 🟢
📋 System Log (Last 10 Events)
Date/TimeEventStatus
No events logged yet
ℹ️ System Information
Platform Version
Stock Agent 4 v7.2
Dashboard
Q5D Options Platform
Last Health Check
--
Workflow Schedule
9:35 AM & 4:05 PM ET
Data Source
Tiingo API
Repository
🔧 Troubleshooting Guide
❓ Data Not Updating +
  1. Check GitHub Actions: View Workflows
  2. Verify TIINGO_TOKEN secret is set in repository settings
  3. Check if market was open (no updates on weekends/holidays)
  4. Manually trigger workflow: Actions → Run workflow
  5. Check workflow logs for errors
❓ Dashboard Shows Stale Data +
  1. Hard refresh: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
  2. Clear browser cache
  3. Check if gh-pages branch has latest data
  4. Verify GitHub Pages deployment completed
❓ Workflow Failing +
  1. Check Actions tab for error messages
  2. Common issues: API rate limits, invalid token, network timeouts
  3. Verify Python dependencies are installed
  4. Check if Tiingo API is operational
🧠
Gann Trading Assistant
Tunnel Through the Air • Mystifying Square • Divine Proportions
Welcome to the Gann Trading Assistant! 🎯 I'm trained on W.D. Gann's principles from "Tunnel Through the Air", the Mystifying Square (Square of 9), and Divine Proportions (Fibonacci/Golden Ratio). I can help you with: • 📊 Analyze any ETF/stock on the dashboard • 🎯 Find high-consensus trades (3/4, 4/4) • 📈 Recommend optimal strikes • 🔮 Gann time cycles & Square of 9 • 📐 Divine Proportions (Phi/Fibonacci) • 📋 Generate custom reports Try: "Show me all ETFs with 4/4 consensus"

🚀 Quick Actions

📊 Current Context

Symbol:SPY
Price:$675.02
Signal:CALL
Consensus:3/4
Mode:INTRADAY

📋 Daily Trading Analysis

Comprehensive signal confluence with Gann & Fibonacci analysis

📊 MASTER SIGNAL CONFLUENCE

SPY Analysis
+7.5
BULLISH BIAS Confidence: 85%
🤖 Agent Consensus 4/4
All 4 agents agree: CALL signal
🔢 Bar Count +12
12 consecutive bullish bars
📐 Pattern Score +3
Bull flag + Golden cross detected
🔮 Gann Cycle +2
Time cycle supports upside
📐 Fibonacci 0
At 50% retracement level
📊 Volume +1.5
Above average volume confirmed

☀️ Morning Trading Plan

Wednesday, November 26, 2025
Generated at
9:35 AM ET
🎯 Top Trade Recommendations
SPY Daily Timeframe BUY SPY CALL
Consensus: 3/4 agents → CALL
Best Entry
$607.15
Entry Range
$605.20 - $607.15
Stop Loss
$597.40
Target 1
$610.20
Target 2
$613.40
Option Strike
$607 CALL
🔮 Gann Time Cycles & Square of 9

⏰ Time Cycles

• 30-day cycle: Bottoming phase
• 90-day cycle: Mid-cycle thrust
• 180-day cycle: Bullish continuation
• Annual cycle: Q4 strength expected

🔢 Square of 9 Levels

• Resistance 1: $685.00
• Resistance 2: $692.50
• Support 1: $672.00
• Support 2: $665.00
📐 Divine Proportions (Phi/Fibonacci)
23.6% Retrace
$678.50
38.2% Retrace
$675.20
50% Retrace
$672.00
61.8% (Golden)
$668.80
78.6% Retrace
$664.50
161.8% Ext
$695.00
🏆 High Consensus Trades (3/4 or 4/4)
Symbol Signal Consensus Confluence Entry Zone Stop Target Strike
📊 Market Context
Overall Bias
BULLISH
Sector Leaders
XLK, XLF
Risk Level
MODERATE
🧠 AI Trading Summary
Morning Analysis: Market conditions favor bullish positions today. SPY showing strong momentum with 4/4 agent consensus. Key levels to watch: Support at $672 (50% Fib), Resistance at $685 (Square of 9).

Recommended Strategy: Focus on CALL spreads on high-consensus ETFs (SPY, QQQ). Entry on pullbacks to Fibonacci support levels. Time stops align with Gann 30-day cycle completion around Dec 15.

Risk Management: Position size 1-2% per trade. Stop losses below 61.8% Fibonacci level. Take profits at 161.8% extension or when 3+ agents flip to HOLD.

⏱️ TRADING TIMEFRAMES REFERENCE

⚡ INTRADAY
0-3 Days
Charts: 5min, 15min, 1hr
Expiry: 0-3 DTE
📈 SWING
3-14 Days
Charts: 4hr, Daily
Expiry: 7-21 DTE
🎯 POSITION
14-45 Days
Charts: Daily, Weekly
Expiry: 30-60 DTE

🔮 W.D. Gann Master Analysis

"Time is more important than price" - W.D. Gann

Analyzing: SPY (S&P 500 Index) $---
🔮

Gann Turn Analysis - SPY

TIMEFRAME
⚡ Intraday
NEXT TURN DATE
Dec 11, 2025 2:30 PM
DAYS UNTIL TURN
~5 hours
CYCLE LENGTH
90 min cycle
⚡ INTRADAY Analysis: Dec 11, 2025 9:30 AM
PREDICTED TURN
🔻 PULLBACK (TOP FORMING)
TRADE ACTION:
BUY PUT
WHY PULLBACK?
  • Current trend: UP (rallied from $580 → $607)
  • At Gann resistance: $616.90 (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: 21 days up = reversal window
EXPECTED MOVE
Turn Target
$595.00 - $602.00
Move Size
-$10 to -$15 (-1.5% to -2.2%)
📊 Multi-Timeframe Turn Predictions
Timeframe Next Turn Prediction Action Confidence
⚡ Intraday Dec 11, 2:30 PM 🔻 PULLBACK BUY PUT
72%
📊 Swing Dec 21, 2025 🔻 PULLBACK BUY PUT
85%
🎯 Position Jan 20, 2026 🔺 RALLY BUY CALL
68%
📐 Square of 9 Calculator
Enter price to generate Square of 9

🟢 SUPPORT LEVELS

-45°:$602.15
-90°:$597.28
-180°:$587.54
-360°:$568.06

🔴 RESISTANCE LEVELS

+45°:$612.03
+90°:$616.90
+180°:$626.64
+360°:$646.12
⏰ Gann Time Cycles

📅 Sacred Calendar Dates

Winter Solstice: Dec 21
Spring Equinox: Mar 20
Summer Solstice: Jun 21
Fall Equinox: Sep 22
7-Day Cycle Day 3 of 7
30-Day Cycle Day 22 of 30
90-Day Cycle Day 68 of 90
Annual Cycle Day 345 of 365
📊 Gann Angles from Pivot
Last Significant Low
$479.05 (Oct 27)
Last Significant High
$609.07 (Dec 6)

📈 Angles from Low (Bullish)

1x1
$523.05
2x1
$567.05
4x1
$655.05
1x2
$501.05
1x4
$490.05
8x1
$831.05

📉 Angles from High (Bearish)

1x1
$605.07
2x1
$601.07
4x1
$593.07
1x2
$607.07
1x4
$608.07
8x1
$577.07
Current Position: Above 2x1 from Low - Strong Bullish
🔢 Sacred Numbers & Proportions

Cardinal Numbers (Degrees)

90°
$597.28
180°
$587.54
270°
$577.80
360°
$568.06

Sacred Numbers

7
$614.09
9
$616.09
12
$619.09
52
$659.09
144
$751.09

Price Mathematical Relationships

√Price:24.68
Price²:371,121
Price ÷ 9:$67.68
Price × 9:$5,481.63
⚡ Master Time & Price Confluence
75
/ 100
Strong Bullish Confluence
Price above 2x1 Gann Angle +20 pts
Near Sq9 +90° Resistance +15 pts
30-Day Cycle Maturing +15 pts
Winter Solstice Approaching +15 pts
Annual Cycle Near Completion +10 pts
🎯 Gann Forecast
GANN DIRECTIONAL BIAS
BULLISH
Next Support (Sq9)
$602.15
Next Resistance (Sq9)
$616.90
Next Cycle Turn
Dec 21, 2025
Days Until
11 days
PROJECTED MOVE
Expect +$9.81 move within 7 days to $616.90
"The future is but a repetition of the past." - W.D. Gann
📊 Multi-Timeframe Chart Analysis
📈 Multi-Timeframe Bias Summary
⚡ Intraday
BULLISH
📈 Swing
BULLISH
🎯 Position
BEARISH
🔥 Confluence
2/3 BULLISH
Intraday CALL
Showing: 15 Minute data
SPY --
Loading chart...
EMA 9
EMA 21
EMA 50
📋 ⚡ INTRADAY TRADE SETUP BUY CALL
Current Trend
📈 RALLY
Trend Timeframe
Daily
💡 Recommendation
Enter on breakout above resistance
Strike
$46 CALL
Entry
$0.45 - $0.55
Expiry
Dec 20
Stop Loss
$44.50
Target 1
$47.50
Target 2
$49.00
Risk/Reward
1:2.5
📈 Swing CALL
Showing: Daily data
SPY --
Loading chart...
EMA 9
EMA 21
EMA 50
📋 📈 SWING TRADE SETUP BUY CALL
Current Trend
📈 RALLY
Trend Timeframe
Daily
💡 Recommendation
Enter on breakout above resistance
Strike
$46 CALL
Entry
$0.45 - $0.55
Expiry
Dec 20
Stop Loss
$44.50
Target 1
$47.50
Target 2
$49.00
Risk/Reward
1:2.5
🎯 Position PUT
Showing: Monthly data
SPY --
Loading chart...
EMA 9
EMA 21
EMA 50
📋 🎯 POSITION TRADE SETUP BUY CALL
Current Trend
📈 RALLY
Trend Timeframe
Daily
💡 Recommendation
Enter on breakout above resistance
Strike
$46 CALL
Entry
$0.45 - $0.55
Expiry
Dec 20
Stop Loss
$44.50
Target 1
$47.50
Target 2
$49.00
Risk/Reward
1:2.5
📋 Chart Type Guide

⚡ Intraday (5-15 min)

Short-term scalping & day trading. Best for 0-1 DTE options.

📈 Swing (Daily/Weekly)

Medium-term swing trades. Best for 3-14 DTE options.

🎯 Position (Monthly)

Long-term position trades. Best for 30+ DTE options.

📊 Candlestick

Traditional OHLC candles showing price action.

🔥 Heikin Ashi

Smoothed candles that filter noise.

📈 Line

Simple line chart for trend clarity.

⭕ Point & Figure

X's and O's with pattern detection.

🧮 Options Profit Calculator

💰 Option Valuation

Option Price
$0.00
Total Cost
$0.00
Intrinsic Value
$0.00
Extrinsic Value
$0.00

📊 Profit and Loss

Breakeven
$0.00
Max Loss
$0.00
Max Profit
Unlimited
Risk/Reward
--

🔮 The Greeks

Delta
0.00
Gamma
0.00
Theta
0.00
Vega
0.00
📚 Greeks Quick Reference
DeltaPrice change per $1 move in stock
GammaRate of change in Delta per $1 move
ThetaDaily time decay (premium lost per day)
VegaPrice change per 1% change in IV

📊 Multi-Timeframe Bias Log

⚡ INTRADAY
15 Min / 1 Hour
BULLISH
Updated: 11:30 AM
📈 SWING
Daily / 4 Hour
BULLISH
Updated: Dec 10
🏦 POSITION
Weekly / Monthly
BULLISH
Updated: Dec 6
🎯 CONFLUENCE
All Timeframes
3/3
ALIGNED BULLISH

📋 Bias Change History

DATE TIME TIMEFRAME PREV BIAS NEW BIAS TRIGGER

📓 Trade Journal

Total Trades
47
Win Rate
68%
Avg Win
+$342
Avg Loss
-$185
Profit Factor
1.85
Total P&L
+$4,280
DATE SYMBOL TYPE ENTRY EXIT P&L RESULT NOTES
Dec 10 SPY CALL $2.45 $3.20 +$375 WIN 3/4 agent consensus, hit T1
Dec 9 IWM PUT $1.85 $2.60 +$225 WIN Gann resistance rejection
Dec 6 QQQ CALL $3.10 $2.40 -$210 LOSS Stopped out on gap down

✅ PRE-TRADE CHECKLIST (All Must Be Met)

📈 FOR CALLS (Long)

☐ Price at Gann 1x1 angle support from major low
☐ Within 3 days of cycle turn date
☐ Price above 50% retracement level
☐ Square of 9 support within 1%
☐ Volume above average
☐ Master Confluence Score ≥ 50/100
☐ Market Guard Rail 2/3 or 3/3

📉 FOR PUTS (Short)

☐ Price at Gann 1x1 angle resistance from major high
☐ Within 3 days of cycle turn date
☐ Price below 50% retracement level
☐ Square of 9 resistance within 1%
☐ Volume above average
☐ Master Confluence Score ≥ 50/100
☐ Market Guard Rail 2/3 or 3/3

💰 POSITION SIZING (Gann Rules)

Per Trade Risk
3.125%
(1/32 of account)
Max All Positions
12.5%
(1/8 of account)
Pyramid Units
3 Units
(1/3 at entry, 1/3 at +12.5%, 1/3 at +25%)

🛑 STOP LOSS PLACEMENT

• Place stop below nearest 1/8 vibration level (12.5% divisions)
• Or below Square of 9 support level
• Never risk more than 3.125% of account per trade
• Move stop to breakeven after +25% profit

📜 W.D. Gann Trading Rules

⏰ Time Rules

  1. Time is more important than price - Wait for cycle dates
  2. Markets change trend every 30, 60, 90 days
  3. Watch for turns at anniversaries of major highs/lows
  4. Seasonal changes - Equinoxes and Solstices matter
  5. 7-day and 52-week cycles are critical

💰 Price Rules

  1. Price moves in squares - Use Square of 9
  2. Support/Resistance at 45 degree angles from pivot
  3. Divide range by 8ths - Key retracement levels
  4. Watch 50% retracement - Critical support/resistance
  5. Price crosses its own square = major reversal

📋 Gann's 28 Trading Rules

1. Never risk more than 10% of capital on single trade
2. Always use stop-loss orders
3. Never overtrade
4. Never let profit turn into loss
5. Don't enter if unsure - wait for clear signal
6. Trade with the trend
7. When in doubt, get out
8. Trade active markets only
9. Never average a losing position
10. Never get out without a good reason
11. Bank surplus profits in reserve
12. Never buy/sell just for a scalp
13. Never move stop loss against you
14. Don't get out prematurely
15. Avoid small trades, go for big moves
16. Don't close trades without reason
17. Add to position only when trend confirmed
18. Never hedge a losing position
19. Never change position without reason
20. Avoid increasing bets after wins
21. Don't guess tops or bottoms
22. Never follow blind tips
23. Reduce trading after first loss
24. Avoid wrong entries and exits
25. Take equal sized profits
26. Never cancel a stop loss
27. Avoid frequent entries/exits
28. Be willing to make money both ways

🔢 Square of 9 Quick Reference

How to calculate:

  • Find square root of current price: sqrt(687) = 26.21
  • Add/subtract degrees: 45deg = 0.25, 90deg = 0.5, 180deg = 1.0, 360deg = 2.0
  • Square the result for next level
  • Example: 26.21 + 0.25 = 26.46 squared = $700.13 (45deg up)
  • Example: 26.21 - 0.25 = 25.96 squared = $673.92 (45deg down)

📑 Performance Reports

NET P&L
+$12,450
↑ 23% vs last period
TOTAL TRADES
156
Avg 7.8/day
WIN RATE
71.2%
111W / 45L
SHARPE RATIO
2.34
Risk-adjusted return

🎯 Performance by Signal Type

Signal Trades Win Rate P&L
ULTRA (4/4) 23 89% +$5,230
SUPER (3/4) 58 78% +$4,890
Standard (2/4) 52 62% +$1,980
Weak (1/4) 23 48% +$350

📊 Performance by Symbol

Symbol Trades Win Rate P&L
SPY 78 74% +$6,450
QQQ 45 71% +$3,280
IWM 22 65% +$1,890
XLF 11 72% +$830

🤖 Agent Performance Breakdown

Base Confluence
68%
Win Rate
+$3,120 P&L
Gann-Elliott
72%
Win Rate
+$4,560 P&L
DQN (ML)
71%
Win Rate
+$3,890 P&L
3-Wave
66%
Win Rate
+$2,780 P&L

🧮 Advanced Options Calculator

THEORETICAL PRICE
$4.82
Black-Scholes Model
INTRINSIC VALUE
$0.00
In/Out of Money
TIME VALUE
$4.82
Extrinsic Premium
BREAK EVEN
$614.82
At Expiration

📊 The Greeks

DELTA (Δ)
0.52
$52 per $1 move
GAMMA (Γ)
0.018
Delta acceleration
THETA (Θ)
-0.42
-$42/day decay
VEGA (ν)
0.85
$85 per 1% IV
RHO (ρ)
0.08
Interest rate sens.

📈 P&L Scenarios at Expiration

Price Move Underlying Option P&L % Return
+3% $625.37 +$1,055 +219%
+2% $619.29 +$447 +93%
+1% $613.22 +$140 +29%
0% $607.15 -$482 -100%
-1% $601.08 -$482 -100%

⏱️ Time Decay Analysis

Days Left Option Value Daily Decay Cumulative
7 (Today) $4.82 -$0.42 $0.00
5 $3.98 -$0.52 -$0.84
3 $2.94 -$0.68 -$1.88
1 $1.58 -$0.95 -$3.24
0 (Expiry) $0.00 - -$4.82
+ strike + ' ' + direction, entry: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
'; html += '
' + degrees[i] + '°
'; html += '
$' + values[i].toFixed(2) + '
'; html += '
'; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
'; html += '' + f.text + ''; html += '' + f.points + ''; html += '
'; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
${escapeHtml(message)}
`; input.value = ''; messagesDiv.innerHTML += `
`; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
${response}
`; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
Sorry, I encountered an error. Please try again.
`; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

Log closed trades to see performance breakdown

'; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
'; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
`; }); html += '
'; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

Log closed trades to see CALL vs PUT performance

'; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
📈 CALLS
${calls.length} trades | ${callWinRate}% win rate
${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
📉 PUTS
${puts.length} trades | ${putWinRate}% win rate
${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
`; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
No issues detected - system running normally
`; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
${iconMap[alert.type] || 'ℹ️'} ${alert.message}
`; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
R: $${fib.high.toFixed(2)}
38.2%: $${fib.level382.toFixed(2)}
61.8%: $${fib.level618.toFixed(2)}
S: $${fib.low.toFixed(2)}
`; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
Loading data...
'; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
Market Snapshot
${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
Trade Setup
Signal: ${optionType}
Entry Zone: ${entryZone}
Stop Loss: $${stopLoss}
Target 1: $${target1}
Target 2: $${target2}
Volume Analysis
Today's volume is at ${volRatio}% of 20-day average.
${volMsg}
Confidence
${confidence}%
${confMsg}
ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
`; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
Insufficient data for pattern detection
'; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
(showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
(showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
No significant patterns detected in last 30 days
`; return; } let html = '
'; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
${result.icon} ${result.text}
${result.detail}
`; }); html += '
'; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
-' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
'; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
+' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
'; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
${angle.name}
$${angle.price.toFixed(2)}
`).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
${angle.name}
$${angle.price.toFixed(2)}
`).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
${num.number}
$${num.nearestLevel.toFixed(2)}
`).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
CENTER
$${price.toFixed(2)}
√${levels.sqrt.toFixed(4)}
${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
${angle}°
`; }).join('')}
`; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
`).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
`).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (atr * 0.3).toFixed(2) + ' - symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (atr * 0.5).toFixed(2), expiry: getTradeExpiryDate(1), stop: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2)), target1: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price + atr * 0.75).toFixed(2) : (price - atr * 0.75).toFixed(2)), target2: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price + atr * 1.25).toFixed(2) : (price - atr * 1.25).toFixed(2)), riskReward: '1:1.5' }; // SWING Setup (3-14 DTE) var swingSetup = { trend: currentTrend, trendTF: 'Daily', recommendation: isBullish ? 'Enter on daily close above 20 SMA' : 'Enter on daily close below 20 SMA', action: 'BUY ' + direction, strike: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + strike + ' ' + direction, entry: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (atr * 0.8).toFixed(2) + ' - symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (atr * 1.2).toFixed(2), expiry: getTradeExpiryDate(10), stop: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price - atr * 1.5).toFixed(2) : (price + atr * 1.5).toFixed(2)), target1: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price + atr * 2).toFixed(2) : (price - atr * 2).toFixed(2)), target2: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price + atr * 3.5).toFixed(2) : (price - atr * 3.5).toFixed(2)), riskReward: '1:2.0' }; // POSITION Setup (30+ DTE) var positionSetup = { trend: currentTrend, trendTF: 'Weekly', recommendation: isBullish ? 'Enter on weekly close above 50 SMA' : 'Enter on weekly close below 50 SMA', action: 'BUY ' + direction, strike: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + strike + ' ' + direction, entry: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (atr * 2).toFixed(2) + ' - symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (atr * 3).toFixed(2), expiry: getTradeExpiryDate(35), stop: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price - atr * 3).toFixed(2) : (price + atr * 3).toFixed(2)), target1: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price + atr * 5).toFixed(2) : (price - atr * 5).toFixed(2)), target2: 'symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); } + (isBullish ? (price + atr * 8).toFixed(2) : (price - atr * 8).toFixed(2)), riskReward: '1:2.5' }; // Update DOM for each timeframe updateTimeframeSetup('intraday', intradaySetup, actionBg); updateTimeframeSetup('swing', swingSetup, actionBg); updateTimeframeSetup('position', positionSetup, actionBg); console.log('Gann Trade Setups updated for ' + symbol); } function updateTimeframeSetup(tf, setup, actionBg) { var el; el = document.getElementById(tf + 'TradeAction'); if (el) { el.textContent = setup.action; el.style.background = actionBg; } el = document.getElementById(tf + 'CurrentTrend'); if (el) { el.textContent = setup.trend.text; el.style.color = setup.trend.color; } el = document.getElementById(tf + 'TrendTF'); if (el) el.textContent = setup.trendTF; el = document.getElementById(tf + 'Recommendation'); if (el) el.textContent = setup.recommendation; el = document.getElementById(tf + 'Strike'); if (el) el.textContent = setup.strike; el = document.getElementById(tf + 'Entry'); if (el) el.textContent = setup.entry; el = document.getElementById(tf + 'Expiry'); if (el) el.textContent = setup.expiry; el = document.getElementById(tf + 'Stop'); if (el) el.textContent = setup.stop; el = document.getElementById(tf + 'Target1'); if (el) el.textContent = setup.target1; el = document.getElementById(tf + 'Target2'); if (el) el.textContent = setup.target2; el = document.getElementById(tf + 'RiskReward'); if (el) el.textContent = setup.riskReward; } function getTradeExpiryDate(daysOut) { var today = new Date(); var expiry = new Date(today.getTime() + daysOut * 24 * 60 * 60 * 1000); // Move to Friday if needed var day = expiry.getDay(); if (day === 0) expiry.setDate(expiry.getDate() + 5); else if (day === 6) expiry.setDate(expiry.getDate() + 6); else if (day !== 5) { var daysToFriday = 5 - day; expiry.setDate(expiry.getDate() + daysToFriday); } return expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function handleGlobalSymbolChange(symbol) { console.log('Global symbol change to: ' + symbol); // Update current symbol variable currentSymbol = symbol; // Update header display if (typeof updateHeaderDisplay === 'function') { updateHeaderDisplay(symbol); } if (typeof updateHeaderForSymbol === 'function') { updateHeaderForSymbol(symbol); } // Update Gann Analysis - THIS IS THE KEY FIX if (typeof updateGannAnalysisForSymbol === 'function') { updateGannAnalysisForSymbol(symbol); } // Update Gann Forecast if (typeof updateGannForecast === 'function') { updateGannForecast(symbol); } // Update Top Trade Recommendation if (typeof updateTopTradeRecommendation === 'function') { updateTopTradeRecommendation(symbol); } // Update Multi-Charts if (typeof updateMultiChartsForSymbol === 'function') { updateMultiChartsForSymbol(symbol); } // Update MTF Bias Summary if (typeof updateMTFBiasSummary === 'function') { updateMTFBiasSummary(symbol); } // Update Overview if (typeof updateOverviewForSymbol === 'function') { updateOverviewForSymbol(symbol); } // Update Opportunities if (typeof updateOpportunitiesForSymbol === 'function') { updateOpportunitiesForSymbol(symbol); } // Update Daily Analysis if (typeof updateDailyAnalysisForSymbol === 'function') { updateDailyAnalysisForSymbol(symbol); } // Update Trade Setup if (typeof updateTradeSetupForSymbol === 'function') { updateTradeSetupForSymbol(symbol); } // Update MTF Trend if (typeof updateMTFTrendForSymbol === 'function') { updateMTFTrendForSymbol(symbol); } // Update Fibonacci Levels if (typeof updateFibonacciForSymbol === 'function') { updateFibonacciForSymbol(symbol); } // Update Cardinal Numbers if (typeof updateCardinalNumbers === 'function') { updateCardinalNumbers(symbol); } // Update Master Confluence if (typeof updateMasterConfluence === 'function') { updateMasterConfluence(symbol); } // Update Technical Patterns if (typeof updateTechnicalPatterns === 'function') { updateTechnicalPatterns(symbol); } // Update Top Trade if (typeof updateTopTrade === 'function') { updateTopTrade(symbol); } // Update Market Conditions if (typeof updateMarketConditions === 'function') { updateMarketConditions(); } // Update MTF Bias History if (typeof updateMTFBiasHistory === 'function') { updateMTFBiasHistory(symbol); } console.log('All sections updated for ' + symbol); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Fibonacci Levels for selected symbol function updateFibonacciForSymbol(symbol) { console.log('Updating Fibonacci for: ' + symbol); // Update the symbol display const sigFibSymbol = document.getElementById('sigFibSymbol'); if (sigFibSymbol) sigFibSymbol.textContent = symbol; // Get symbol data var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Calculate Fibonacci levels from a recent swing const swingHigh = price * 1.05; const swingLow = price * 0.95; const range = swingHigh - swingLow; // Fibonacci levels const fib382 = swingLow + (range * 0.382); const fib500 = swingLow + (range * 0.5); const fib618 = swingLow + (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); // Find nearest level const levels = [ { name: 'R3', price: fib1618, type: 'resistance' }, { name: 'R2', price: fib1272, type: 'resistance' }, { name: 'R1', price: fib100, type: 'resistance' }, { name: 'S1', price: fib618, type: 'support' }, { name: 'S2', price: fib500, type: 'support' }, { name: 'S3', price: fib382, type: 'support' } ]; let nearest = levels[0]; let minDist = Math.abs(price - levels[0].price); for (const lvl of levels) { const dist = Math.abs(price - lvl.price); if (dist < minDist) { minDist = dist; nearest = lvl; } } // Update the table const sigFibTable = document.getElementById('sigFibTable'); if (sigFibTable) { const dates = ['Nov 15', 'Nov 15', 'Dec 1', '', 'Dec 1', 'Nov 15', 'Nov 15']; const tfs = ['Weekly', 'Daily', 'Daily', '', 'Daily', 'Daily', 'Weekly']; const impls = ['Strong Resistance - Major Target', 'Resistance - Take Partial Profits', 'First Resistance', 'Current Price Level', 'First Support - Bounce Zone', 'Support - Consider Adding', 'Strong Support - Major Buy Zone']; const rows = [ { level: 'R3 (161.8%)', price: fib1618, dist: ((fib1618 - price) / price * 100), color: '#22c55e', idx: 0 }, { level: 'R2 (127.2%)', price: fib1272, dist: ((fib1272 - price) / price * 100), color: '#22c55e', idx: 1 }, { level: 'R1 (100%)', price: fib100, dist: ((fib100 - price) / price * 100), color: '#f59e0b', idx: 2 }, { level: 'CURRENT', price: price, dist: 0, color: '#3b82f6', idx: 3, isCurrent: true }, { level: 'S1 (38.2%)', price: fib382, dist: ((fib382 - price) / price * 100), color: '#eab308', idx: 4 }, { level: 'S2 (50%)', price: fib500, dist: ((fib500 - price) / price * 100), color: '#ef4444', idx: 5 }, { level: 'S3 (61.8%)', price: fib618, dist: ((fib618 - price) / price * 100), color: '#ef4444', idx: 6 } ]; var html = ''; for (const r of rows) { const bg = r.isCurrent ? 'background: rgba(59,130,246,0.1);' : (r.level.includes('R1') ? 'background: rgba(245,158,11,0.1);' : ''); const nearTag = Math.abs(r.dist) < 1 && !r.isCurrent ? '⚠️ NEAR' : (r.isCurrent ? '📍 HERE' : '-'); const distStr = r.isCurrent ? '--' : (r.dist >= 0 ? '+' : '') + r.dist.toFixed(1) + '%'; const distColor = r.isCurrent ? '' : (r.dist >= 0 ? '#22c55e' : '#ef4444'); html += ''; html += '' + r.level + ''; html += '$' + r.price.toFixed(2) + ''; html += '' + distStr + ''; html += '' + nearTag + ''; html += '' + (dates[r.idx] || '--') + ''; html += '' + (tfs[r.idx] || '--') + ''; html += '' + impls[r.idx] + ''; html += ''; } sigFibTable.innerHTML = html; } // Update Patterns tab Fibonacci elements const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); } // Update Cardinal Numbers for selected symbol function updateCardinalNumbers(symbol) { console.log('Updating Cardinal Numbers for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for Cardinal Numbers: ' + symbol); return; } var price = parseFloat(data.price); var sqrt = Math.sqrt(price); // Cardinal Cross from Square of 9 // These are resistance levels at 90 degree intervals above current price var cardinal = { deg90: Math.pow(sqrt + 0.25, 2), deg180: Math.pow(sqrt + 0.5, 2), deg270: Math.pow(sqrt + 0.75, 2), deg360: Math.pow(sqrt + 1.0, 2) }; // Update the cardinalNumbers element var cardinalDiv = document.getElementById('cardinalNumbers'); if (cardinalDiv) { var html = ''; var degrees = [90, 180, 270, 360]; var values = [cardinal.deg90, cardinal.deg180, cardinal.deg270, cardinal.deg360]; for (var i = 0; i < degrees.length; i++) { html += '
    '; html += '
    ' + degrees[i] + '°
    '; html += '
    $' + values[i].toFixed(2) + '
    '; html += '
    '; } cardinalDiv.innerHTML = html; } console.log('Cardinal Numbers updated for ' + symbol + ': 90°=$' + cardinal.deg90.toFixed(2)); } // Update Master Confluence for selected symbol function updateMasterConfluence(symbol) { console.log('Updating Master Confluence for: ' + symbol); var data = symbolGannData[symbol] || symbolGannData['SPY']; if (!data || !data.price) return; var price = parseFloat(data.price); var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; var score = 0; var factors = []; // Factor 1: Price near Square of 9 level (max 25) var sqrt = Math.sqrt(price); var sq9Support = Math.pow(sqrt - 0.5, 2); var sq9Resistance = Math.pow(sqrt + 0.5, 2); var distSupport = Math.abs(price - sq9Support) / price; var distResist = Math.abs(price - sq9Resistance) / price; var minDist = Math.min(distSupport, distResist); if (minDist < 0.01) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: '+25 pts', color: '#22c55e' }); } else if (minDist < 0.03) { score += 15; factors.push({ text: 'Price near Square of 9 level', points: '+15 pts', color: '#22c55e' }); } else if (minDist < 0.05) { score += 10; factors.push({ text: 'Price approaching Sq9 level', points: '+10 pts', color: '#eab308' }); } // Factor 2: Time near cycle turn date (max 25) var today = new Date(); var dayOfMonth = today.getDate(); var gannDates = [7, 14, 21, 28]; var nearCycle = false; for (var i = 0; i < gannDates.length; i++) { var diff = Math.abs(dayOfMonth - gannDates[i]); if (diff <= 2) { score += 25; factors.push({ text: 'Time near cycle turn date', points: '+25 pts', color: '#22c55e' }); nearCycle = true; break; } else if (diff <= 5) { score += 15; factors.push({ text: 'Time approaching cycle turn', points: '+15 pts', color: '#22c55e' }); nearCycle = true; break; } } // Factor 3: Price on Gann angle (max 25) var range = high - low; if (range > 0) { var position = (price - low) / range; var gannAngles = [0.25, 0.333, 0.5, 0.618, 0.75]; for (var j = 0; j < gannAngles.length; j++) { var angleDiff = Math.abs(position - gannAngles[j]); if (angleDiff < 0.03) { score += 25; factors.push({ text: 'Price on Gann angle (' + Math.round(gannAngles[j]*100) + '%)', points: '+25 pts', color: '#22c55e' }); break; } else if (angleDiff < 0.08) { score += 15; factors.push({ text: 'Price near Gann angle', points: '+15 pts', color: '#22c55e' }); break; } } } // Factor 4: Multiple cycles converging (max 25) var cycleCount = factors.length; if (cycleCount >= 3) { score += 25; factors.push({ text: 'Multiple cycles converging', points: '+25 pts', color: '#22c55e' }); } else if (cycleCount >= 2) { score += 15; factors.push({ text: 'Two cycles aligning', points: '+15 pts', color: '#8b5cf6' }); } else if (cycleCount >= 1) { score += 10; factors.push({ text: 'Single cycle active', points: '+10 pts', color: '#8b5cf6' }); } // Cap at 100 score = Math.min(100, score); // Determine label and color var label, color; if (score >= 75) { label = 'HIGH PROBABILITY'; color = '#22c55e'; } else if (score >= 50) { label = 'MEDIUM PROBABILITY'; color = '#f59e0b'; } else { label = 'LOW PROBABILITY'; color = '#ef4444'; } // Update the UI elements var scoreEl = document.getElementById('confluenceScore'); var labelEl = document.getElementById('confluenceLabel'); var ringEl = document.getElementById('confluenceRing'); var factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = score; scoreEl.style.color = color; } if (labelEl) { labelEl.textContent = label; labelEl.style.color = color; } if (ringEl) { var offset = 314 - (314 * score / 100); ringEl.setAttribute('stroke-dashoffset', offset); ringEl.setAttribute('stroke', color); } if (factorsEl) { var html = ''; for (var k = 0; k < factors.length; k++) { var f = factors[k]; var bgColor = f.color === '#22c55e' ? '34,197,94' : f.color === '#eab308' ? '234,179,8' : '139,92,246'; html += '
    '; html += '' + f.text + ''; html += '' + f.points + ''; html += '
    '; } factorsEl.innerHTML = html; } // Update master confluence score in overview var masterScoreEl = document.getElementById('masterConfluenceScore'); if (masterScoreEl) { masterScoreEl.textContent = '+' + (score / 10).toFixed(1); masterScoreEl.style.color = color; } } // Update Technical Patterns for selected symbol function updateTechnicalPatterns(symbol) { console.log('Updating Technical Patterns for: ' + symbol); var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No pattern data for: ' + symbol); return; } var price = parseFloat(data.price); // Generate patterns based on symbol/price var patterns = [ { name: 'Double Bottom', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 8', timeframe: 'Daily', confidence: 72, target: (price * 1.03).toFixed(2), implication: 'Reversal confirmed at support', status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Ascending Triangle', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 6', timeframe: '4-Hour', confidence: 65, target: (price * 1.02).toFixed(2), implication: 'Breakout pending above $' + (price * 1.01).toFixed(0), status: 'ACTIVE', statusColor: '#22c55e', statusBg: 'rgba(34,197,94,0.2)' }, { name: 'Bullish Engulfing', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 10', timeframe: 'Daily', confidence: 78, target: (price * 1.018).toFixed(2), implication: 'Momentum shift confirmed', status: 'FORMING', statusColor: '#eab308', statusBg: 'rgba(234,179,8,0.2)' }, { name: 'Inv Head & Shoulders', type: 'Bullish', typeColor: '#22c55e', date: 'Dec 3', timeframe: 'Weekly', confidence: 58, target: (price * 1.05).toFixed(2), implication: 'Major reversal potential', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' }, { name: 'Rising Wedge', type: 'Bearish', typeColor: '#ef4444', date: 'Dec 5', timeframe: '4-Hour', confidence: 45, target: (price * 0.98).toFixed(2), implication: 'Potential pullback setup', status: 'WATCH', statusColor: '#3b82f6', statusBg: 'rgba(59,130,246,0.2)' } ]; // Update the table var tableBody = document.getElementById('technicalPatternsTable'); if (tableBody) { let html = ''; for (var i = 0; i < patterns.length; i++) { var p = patterns[i]; html += ''; html += '' + p.name + ''; html += '' + p.type + ''; html += '' + p.date + ''; html += '' + p.timeframe + ''; html += '' + p.confidence + '%'; html += '$' + p.target + ''; html += '' + p.implication + ''; html += '' + p.status + ''; html += ''; } tableBody.innerHTML = html; } // Update the symbol display var patternSymbol = document.getElementById('patternSymbol'); if (patternSymbol) patternSymbol.textContent = symbol; // Update hardcoded pattern breakout/target values in HTML var breakout1 = document.getElementById('patternBreakout1'); var breakout2 = document.getElementById('patternBreakout2'); var target1 = document.getElementById('patternTarget1'); if (breakout1) breakout1.textContent = '$' + (price * 1.01).toFixed(0); if (breakout2) breakout2.textContent = '$' + (price * 0.99).toFixed(0); if (target1) target1.textContent = '$' + (price * 1.02).toFixed(0); console.log('Technical patterns updated for ' + symbol + ' at $' + price); } // Attach global symbol change handler to ALL symbol dropdowns on page load document.addEventListener('DOMContentLoaded', function() { // Find main symbol dropdown const mainDropdown = document.getElementById('symbolSelect'); if (mainDropdown) { mainDropdown.addEventListener('change', function() { handleGlobalSymbolChange(this.value); }); } // Initial load - update for current symbol after a delay setTimeout(function() { const initialSymbol = mainDropdown?.value || currentSymbol || 'SPY'; handleGlobalSymbolChange(initialSymbol); updateTopTrade(initialSymbol); }, 500); }); // ============================================ // MASTER REFRESH FUNCTION // ============================================ // Master refresh function with comprehensive error handling async function refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); const refreshIcon = document.getElementById('refreshIcon'); // Get current symbol from multiple possible dropdowns const symbolDropdown = document.getElementById('symbolSelect') || document.querySelector('header select') || document.querySelector('[id*="symbol"]'); const symbol = symbolDropdown ? symbolDropdown.value : 'SPY'; currentSymbol = symbol; console.log('🔄 Refreshing all data for: ' + symbol); // Show loading state if (refreshBtn) { refreshBtn.disabled = true; refreshBtn.style.opacity = '0.7'; } if (refreshIcon) { refreshIcon.innerHTML = '⏳'; } showNotification('Refreshing ' + symbol + '...', 'info'); try { // Overview sections if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(symbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(symbol); if (typeof updatePatternDetection === 'function') updatePatternDetection(symbol); if (typeof populatePatternTable === 'function') populatePatternTable(symbol); if (typeof updateHistoricalData === 'function') updateHistoricalData(symbol); if (typeof populateHistoricalTable === 'function') populateHistoricalTable(symbol); if (typeof updateFibonacciLevels === 'function') updateFibonacciLevels(symbol); if (typeof populateFibonacciTable === 'function') populateFibonacciTable(symbol); // Gann sections if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(symbol); if (typeof updateGannForecast === 'function') updateGannForecast(symbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(symbol); if (typeof updateSq9Calculator === 'function') updateSq9Calculator(symbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(symbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(symbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(symbol); if (typeof updateGannAngles === 'function') updateGannAngles(symbol); // Bar Count if (typeof updateBarCount === 'function') updateBarCount(symbol); if (typeof updateBarCountHistory === 'function') updateBarCountHistory(symbol); if (typeof populateBarCountTable === 'function') populateBarCountTable(symbol); // Signals & Patterns if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(symbol); if (typeof updatePatternsForSymbol === 'function') updatePatternsForSymbol(symbol); if (typeof updateTechnicalPatterns === 'function') updateTechnicalPatterns(symbol); // Today stats if (typeof updateTodayStats === 'function') updateTodayStats(symbol); if (typeof updateTodayVsAverage === 'function') updateTodayVsAverage(symbol); if (typeof updateMarketStats === 'function') updateMarketStats(symbol); // Other sections if (typeof updateOpportunitiesForSymbol === 'function') updateOpportunitiesForSymbol(symbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(symbol); if (typeof updateMultiChartsForSymbol === 'function') updateMultiChartsForSymbol(symbol); if (typeof updateMTFBiasSummary === 'function') updateMTFBiasSummary(symbol); if (typeof updateTopTradeRecommendation === 'function') updateTopTradeRecommendation(symbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(symbol); if (typeof updateTradeSetupForSymbol === 'function') updateTradeSetupForSymbol(symbol); if (typeof updateMTFTrendForSymbol === 'function') updateMTFTrendForSymbol(symbol); // Reload data if function exists if (typeof loadAllData === 'function') loadAllData(); if (typeof loadSymbolData === 'function') loadSymbolData(symbol); // Update timestamp if (typeof updateLastRefreshTime === 'function') updateLastRefreshTime(); showNotification('✅ ' + symbol + ' fully refreshed!', 'success'); console.log('✅ Refresh complete for ' + symbol); } catch (error) { console.error('Refresh error:', error); showNotification('❌ Error: ' + error.message, 'error'); } finally { // Reset button if (refreshBtn) { refreshBtn.disabled = false; refreshBtn.style.opacity = '1'; } if (refreshIcon) { refreshIcon.innerHTML = '🔄'; } } } // Generic notification function (consolidates showRefreshNotification) function showNotification(message, type) { // Try the existing function first if (typeof showRefreshNotification === 'function') { showRefreshNotification(message, type); return; } // Fallback: create a simple toast notification let toast = document.getElementById('toast-notification'); if (!toast) { toast = document.createElement('div'); toast.id = 'toast-notification'; toast.style.cssText = 'position:fixed;top:70px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;font-size:13px;font-weight:600;animation:slideIn 0.3s ease;box-shadow:0 4px 15px rgba(0,0,0,0.2);'; document.body.appendChild(toast); } toast.textContent = message; if (type === 'success') { toast.style.background = '#22c55e'; toast.style.color = 'white'; } else if (type === 'error') { toast.style.background = '#ef4444'; toast.style.color = 'white'; } else { toast.style.background = '#3b82f6'; toast.style.color = 'white'; } toast.style.display = 'block'; setTimeout(() => { toast.style.display = 'none'; }, 3000); } // Fetch latest price (uses cached data as Yahoo Finance has CORS issues) async function fetchLatestPrice(symbol) { try { // Try to load from CSV first const response = await fetch('data/' + symbol + '.csv?t=' + Date.now()); if (response.ok) { const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length >= 2) { const lastLine = lines[lines.length - 1]; const columns = lastLine.split(','); const price = parseFloat(columns[4]); // Close price if (!isNaN(price) && symbolGannData[symbol]) { const oldPrice = symbolGannData[symbol].price; symbolGannData[symbol].price = price; const change = price - oldPrice; const changePct = (change / oldPrice) * 100; updatePriceDisplay(symbol, price, change, changePct); console.log(symbol + ': $' + price.toFixed(2) + ' (from CSV)'); return { price, change, changePct }; } } } } catch (error) { console.warn('CSV fetch failed, using cached data:', error); } return null; } // Update header price display function updatePriceDisplay(symbol, price, change, changePct) { const priceEl = document.getElementById('headerPrice'); const changeEl = document.getElementById('headerChange'); if (priceEl) { priceEl.textContent = '$' + price.toFixed(2); } if (changeEl) { const sign = change >= 0 ? '+' : ''; changeEl.textContent = sign + change.toFixed(2) + ' (' + sign + changePct.toFixed(2) + '%)'; changeEl.style.color = change >= 0 ? '#22c55e' : '#ef4444'; } // Also update the symbol banner const symbolBannerPrice = document.querySelector('#symbolBanner .price, .symbol-banner-price'); if (symbolBannerPrice) { symbolBannerPrice.textContent = '$' + price.toFixed(2); } } // Update header for selected symbol function updateHeaderForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update current symbol display const symbolDisplay = document.getElementById('currentSymbolDisplay'); if (symbolDisplay) { symbolDisplay.textContent = symbol; } // Update symbol name const symbolName = document.getElementById('currentSymbolName'); if (symbolName) { symbolName.textContent = data.sector || symbol + ' ETF'; } // Update Gann header const gannSymbol = document.getElementById('gannCurrentSymbol'); if (gannSymbol) { gannSymbol.textContent = symbol; } const gannPrice = document.getElementById('gannCurrentPrice'); if (gannPrice) { gannPrice.textContent = '$' + data.price.toFixed(2); } const gannSector = document.getElementById('gannSectorName'); if (gannSector) { gannSector.textContent = '(' + data.sector + ')'; } } // Update last refresh timestamp function updateLastRefreshTime() { const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // Update data time displays const dataTime = document.getElementById('dataTime'); if (dataTime) { dataTime.textContent = timeStr; } const dataDate = document.getElementById('dataDate'); if (dataDate) { dataDate.textContent = dateStr; } // Update all section timestamps document.querySelectorAll('.section-timestamp').forEach(el => { el.textContent = '⏱️ ' + timeStr; }); } // Show refresh notification toast function showRefreshNotification(message, type) { // Remove existing notification const existing = document.getElementById('refresh-notification-toast'); if (existing) existing.remove(); const colors = { 'info': '#3b82f6', 'success': '#22c55e', 'error': '#ef4444', 'warning': '#f59e0b' }; const notification = document.createElement('div'); notification.id = 'refresh-notification-toast'; notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: ' + (colors[type] || colors.info) + '; color: white; padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 500; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease;'; notification.textContent = message; document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(function() { notification.remove(); }, 300); }, 3000); } // ============================================ // SYMBOL-SPECIFIC UPDATE FUNCTIONS // ============================================ // Update Overview section for symbol function updateOverviewForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update overview price displays const priceEl = document.getElementById('overviewPrice'); if (priceEl) priceEl.textContent = '$' + data.price.toFixed(2); const highEl = document.getElementById('overview52High'); if (highEl) highEl.textContent = '$' + data.high.toFixed(2); const lowEl = document.getElementById('overview52Low'); if (lowEl) lowEl.textContent = '$' + data.low.toFixed(2); console.log('Overview updated for ' + symbol); } // Update Multi-Charts for symbol function updateMultiChartsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update chart symbol labels document.querySelectorAll('.chart-symbol-label').forEach(function(el) { el.textContent = symbol; }); // Update chart price labels document.querySelectorAll('.chart-price-label').forEach(function(el) { el.textContent = '$' + data.price.toFixed(2); }); // Sync multi-chart symbol dropdown const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { multiChartSymbol.value = symbol; } console.log('Multi-Charts updated for ' + symbol); } // Update Opportunities for symbol - highlight matching rows function updateOpportunitiesForSymbol(symbol) { const tbody = document.getElementById('oppTableBody'); if (!tbody) return; tbody.querySelectorAll('tr').forEach(function(row) { const symbolCell = row.cells[1]; if (symbolCell) { const rowSymbol = symbolCell.textContent.trim(); if (rowSymbol === symbol) { row.style.background = 'rgba(59,130,246,0.15)'; row.style.fontWeight = '600'; } else { row.style.background = ''; row.style.fontWeight = ''; } } }); console.log('Opportunities updated for ' + symbol); } // Update Daily Analysis for symbol function updateDailyAnalysisForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Update Morning Plan symbol reference const morningSymbolEl = document.getElementById('morningPlanSymbol'); if (morningSymbolEl) morningSymbolEl.textContent = symbol; // Update confluence symbol const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = symbol + ' Analysis'; // Calculate Square of 9 for entry/stop/targets const sq9 = calculateSquareOf9(data.price); if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const entryEl = document.getElementById('morningPlanEntry'); if (entryEl) entryEl.textContent = '$' + data.price.toFixed(2); const stopEl = document.getElementById('morningPlanStop'); if (stopEl) stopEl.textContent = '$' + support45.toFixed(2); const t1El = document.getElementById('morningPlanT1'); if (t1El) t1El.textContent = '$' + resistance45.toFixed(2); const t2El = document.getElementById('morningPlanT2'); if (t2El) t2El.textContent = '$' + resistance90.toFixed(2); } console.log('Daily Analysis updated for ' + symbol); } // Update Signals for symbol - highlight matching rows function updateSignalsForSymbol(symbol) { document.querySelectorAll('.signals-table tr, #signalsTable tr, #agentSignalsBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Signals updated for ' + symbol); } // Update Patterns for symbol function updatePatternsForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Highlight pattern rows for current symbol document.querySelectorAll('#patternTableBody tr').forEach(function(row) { const symbolCell = row.querySelector('td:first-child'); if (symbolCell && symbolCell.textContent.trim() === symbol) { row.style.background = 'rgba(59,130,246,0.1)'; } else if (symbolCell) { row.style.background = ''; } }); console.log('Patterns updated for ' + symbol); } // Update Trade Setup for symbol function updateTradeSetupForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; const sq9 = calculateSquareOf9(data.price); const isBullish = data.price > (data.high + data.low) / 2; if (sq9) { const support45 = sq9.support && sq9.support[0] ? sq9.support[0].price : data.price * 0.98; const resistance45 = sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : data.price * 1.02; const resistance90 = sq9.resistance && sq9.resistance[1] ? sq9.resistance[1].price : data.price * 1.04; const support90 = sq9.support && sq9.support[1] ? sq9.support[1].price : data.price * 0.96; // Update trade setup elements if they exist const entryLowEl = document.getElementById('tradeEntryLow'); if (entryLowEl) entryLowEl.textContent = '$' + (data.price * 0.995).toFixed(2); const entryHighEl = document.getElementById('tradeEntryHigh'); if (entryHighEl) entryHighEl.textContent = '$' + (data.price * 1.005).toFixed(2); const stopEl = document.getElementById('tradeStop'); if (stopEl) stopEl.textContent = '$' + (isBullish ? support45 : resistance45).toFixed(2); const target1El = document.getElementById('tradeTarget1'); if (target1El) target1El.textContent = '$' + (isBullish ? resistance45 : support45).toFixed(2); const target2El = document.getElementById('tradeTarget2'); if (target2El) target2El.textContent = '$' + (isBullish ? resistance90 : support90).toFixed(2); } console.log('Trade Setup updated for ' + symbol); } // Update MTF Trend for symbol function updateMTFTrendForSymbol(symbol) { const data = symbolGannData[symbol]; if (!data) return; // Determine bias based on price position const upperThird = data.low + (data.high - data.low) * 0.66; const lowerThird = data.low + (data.high - data.low) * 0.33; let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (data.price > upperThird) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (data.price < lowerThird) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BEARISH'; } else { intradayBias = 'NEUTRAL'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } // Update displays const updateBiasDisplay = function(id, bias) { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBiasDisplay('mtfIntradayBias', intradayBias); updateBiasDisplay('mtfSwingBias', swingBias); updateBiasDisplay('mtfPositionBias', positionBias); console.log('MTF Trend updated for ' + symbol); } // Update Top Trade Recommendation - Signal Must Match Agent Consensus function updateTopTradeRecommendation(symbol) { const data = symbolGannData[symbol]; if (!data) return; console.log('Updating Top Trade for: ' + symbol); // Calculate position in range const range = data.high - data.low; const position = (data.price - data.low) / range; // Simulate agent votes based on price position const agentVotes = []; // Base Confluence - trend following if (position > 0.6) { agentVotes.push({ name: 'Base Confluence', signal: 'PUT', confidence: 65 }); } else if (position < 0.4) { agentVotes.push({ name: 'Base Confluence', signal: 'CALL', confidence: 65 }); } else { agentVotes.push({ name: 'Base Confluence', signal: 'HOLD', confidence: 50 }); } // Gann-Elliott - reversal focused if (position > 0.7) { agentVotes.push({ name: 'Gann-Elliott', signal: 'PUT', confidence: 70 }); } else if (position < 0.3) { agentVotes.push({ name: 'Gann-Elliott', signal: 'CALL', confidence: 70 }); } else { agentVotes.push({ name: 'Gann-Elliott', signal: 'HOLD', confidence: 55 }); } // DQN (ML) - momentum based if (position > 0.5) { agentVotes.push({ name: 'DQN (ML)', signal: 'CALL', confidence: 60 }); } else { agentVotes.push({ name: 'DQN (ML)', signal: 'PUT', confidence: 60 }); } // 3-Wave - pattern based if (position > 0.65) { agentVotes.push({ name: '3-Wave', signal: 'PUT', confidence: 62 }); } else if (position < 0.35) { agentVotes.push({ name: '3-Wave', signal: 'CALL', confidence: 62 }); } else { agentVotes.push({ name: '3-Wave', signal: 'HOLD', confidence: 50 }); } // Count votes let callCount = 0, putCount = 0, holdCount = 0; agentVotes.forEach(v => { if (v.signal === 'CALL') callCount++; else if (v.signal === 'PUT') putCount++; else holdCount++; }); // Determine consensus let consensusSignal = 'HOLD'; let consensusText = ''; if (putCount >= 3) { consensusSignal = 'PUT'; consensusText = putCount + '/4 agents → PUT'; } else if (callCount >= 3) { consensusSignal = 'CALL'; consensusText = callCount + '/4 agents → CALL'; } else if (putCount === 2 && callCount < 2) { consensusSignal = 'PUT'; consensusText = '2/4 agents → PUT'; } else if (callCount === 2 && putCount < 2) { consensusSignal = 'CALL'; consensusText = '2/4 agents → CALL'; } else { consensusSignal = 'HOLD'; consensusText = 'No clear consensus'; } // Update UI elements const symbolEl = document.querySelector('#topTradeSymbol, .top-trade-symbol'); if (symbolEl) symbolEl.textContent = symbol; // Signal badge - MUST MATCH CONSENSUS var signalBadge = document.getElementById('topTradeSignalBadge'); if (signalBadge) { signalBadge.textContent = 'BUY ' + symbol + ' ' + consensusSignal; signalBadge.className = 'signal-badge'; if (consensusSignal === 'CALL') { signalBadge.style.background = '#22c55e'; signalBadge.style.color = 'white'; } else if (consensusSignal === 'PUT') { signalBadge.style.background = '#ef4444'; signalBadge.style.color = 'white'; } else { signalBadge.style.background = '#6b7280'; signalBadge.style.color = 'white'; } } // Consensus text const consensusEl = document.querySelector('#agentConsensus, .agent-consensus'); if (consensusEl) { consensusEl.textContent = 'Consensus: ' + consensusText; consensusEl.style.color = consensusSignal === 'CALL' ? '#22c55e' : (consensusSignal === 'PUT' ? '#ef4444' : '#6b7280'); } // Calculate entry/stop/targets based on signal const atr = data.atr || (data.price * 0.015); const sq9 = calculateSquareOf9Levels(data.price); let entry, stop, t1, t2, strike; if (consensusSignal === 'PUT') { entry = data.price; stop = data.price + (atr * 1.5); t1 = sq9.support.deg45; t2 = sq9.support.deg90; strike = Math.round(data.price) + ' PUT'; } else if (consensusSignal === 'CALL') { entry = data.price; stop = data.price - (atr * 1.5); t1 = sq9.resistance.deg45; t2 = sq9.resistance.deg90; strike = Math.round(data.price) + ' CALL'; } else { entry = data.price; stop = data.price - (atr * 1.0); t1 = sq9.resistance.deg45; t2 = sq9.support.deg45; strike = Math.round(data.price) + ' HOLD'; } // Update entry price const entryEl = document.querySelector('#bestEntry, .best-entry'); if (entryEl) entryEl.textContent = '$' + entry.toFixed(2); // Update entry range const rangeEl = document.querySelector('#entryRange, .entry-range'); if (rangeEl) rangeEl.textContent = '$' + (entry - atr * 0.3).toFixed(2) + ' - $' + entry.toFixed(2); // Update stop loss const stopEl = document.querySelector('#stopLoss, .stop-loss'); if (stopEl) stopEl.textContent = '$' + stop.toFixed(2); // Update targets const t1El = document.querySelector('#target1, .target-1'); if (t1El) t1El.textContent = '$' + t1.toFixed(2); const t2El = document.querySelector('#target2, .target-2'); if (t2El) t2El.textContent = '$' + t2.toFixed(2); // Update strike const strikeEl = document.querySelector('#optionStrike, .option-strike'); if (strikeEl) strikeEl.textContent = '$' + strike; console.log('Top Trade updated for ' + symbol + ': ' + consensusSignal); } // Update MTF Bias Summary when symbol changes function updateMTFBiasSummary(symbol) { const data = symbolGannData[symbol]; if (!data) return; const range = data.high - data.low; const position = (data.price - data.low) / range; // Determine biases based on position let intradayBias = 'NEUTRAL'; let swingBias = 'NEUTRAL'; let positionBias = 'NEUTRAL'; if (position > 0.7) { intradayBias = 'BEARISH'; swingBias = 'BEARISH'; positionBias = 'BULLISH'; } else if (position > 0.5) { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else if (position > 0.3) { intradayBias = 'BEARISH'; swingBias = 'BULLISH'; positionBias = 'BULLISH'; } else { intradayBias = 'BULLISH'; swingBias = 'BULLISH'; positionBias = 'NEUTRAL'; } // Update displays const updateBias = (id, bias) => { const el = document.getElementById(id); if (el) { el.textContent = bias; el.style.color = bias === 'BULLISH' ? '#22c55e' : (bias === 'BEARISH' ? '#ef4444' : '#eab308'); } }; updateBias('mtfIntradayBias', intradayBias); updateBias('mtfSwingBias', swingBias); updateBias('mtfPositionBias', positionBias); // Update confluence let bullishCount = 0; if (intradayBias === 'BULLISH') bullishCount++; if (swingBias === 'BULLISH') bullishCount++; if (positionBias === 'BULLISH') bullishCount++; const confluenceEl = document.getElementById('mtfConfluence'); if (confluenceEl) { confluenceEl.textContent = bullishCount + '/3 BULLISH'; confluenceEl.style.color = bullishCount >= 2 ? '#22c55e' : (bullishCount === 0 ? '#ef4444' : '#eab308'); } console.log('MTF Bias Summary updated for ' + symbol); } // Update chart for specific timeframe function updateChartForTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' chart to ' + timeframe); var symbol = currentSymbol || 'SPY'; const data = symbolGannData[symbol]; if (!data) return; // Timeframe-specific data ranges const timeframeConfig = { '1m': { bars: 60, xLabels: ['9:30', '10:00', '10:30', '11:00'], range: 0.005 }, '5m': { bars: 78, xLabels: ['9:30', '11:00', '12:30', '14:00'], range: 0.01 }, '15m': { bars: 26, xLabels: ['9:30', '11:00', '13:00', '15:00'], range: 0.015 }, '30m': { bars: 13, xLabels: ['10:00', '12:00', '14:00', '16:00'], range: 0.02 }, '1h': { bars: 7, xLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], range: 0.025 }, '4h': { bars: 30, xLabels: ['Nov 25', 'Dec 2', 'Dec 9', 'Dec 11'], range: 0.04 }, '1d': { bars: 20, xLabels: ['Nov 12', 'Nov 22', 'Dec 2', 'Dec 11'], range: 0.08 }, '1w': { bars: 12, xLabels: ['Sep', 'Oct', 'Nov', 'Dec'], range: 0.15 }, '1M': { bars: 6, xLabels: ['Jul', 'Sep', 'Nov', 'Jan'], range: 0.25 }, '3M': { bars: 4, xLabels: ['Q1', 'Q2', 'Q3', 'Q4'], range: 0.40 } }; const config = timeframeConfig[timeframe] || timeframeConfig['1d']; // Update "Showing: X data" label const showingLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; const labelEl = document.querySelector('#' + chartType + 'ShowingLabel, .' + chartType + '-showing'); if (labelEl) { labelEl.textContent = 'Showing: ' + showingLabels[timeframe] + ' data'; } // Update x-axis labels const xAxisEl = document.querySelector('#' + chartType + 'XAxis, .' + chartType + '-x-axis'); if (xAxisEl) { const spans = xAxisEl.querySelectorAll('span'); config.xLabels.forEach((label, i) => { if (spans[i]) spans[i].textContent = label; }); } // Show notification showNotification(chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' → ' + showingLabels[timeframe], 'info'); } // Attach handlers to all timeframe dropdowns on load document.addEventListener('DOMContentLoaded', function() { // Intraday dropdown const intradayTf = document.getElementById('intradayTimeframe'); if (intradayTf) { intradayTf.addEventListener('change', function() { updateChartForTimeframe('intraday', this.value); }); } // Swing dropdown const swingTf = document.getElementById('swingTimeframe'); if (swingTf) { swingTf.addEventListener('change', function() { updateChartForTimeframe('swing', this.value); }); } // Position dropdown const positionTf = document.getElementById('positionTimeframe'); if (positionTf) { positionTf.addEventListener('change', function() { updateChartForTimeframe('position', this.value); }); } }); // Also call when Opportunities tab is clicked const oppTab = document.querySelector('[data-tab="opportunities"]'); if (oppTab) { oppTab.addEventListener('click', function() { setTimeout(loadOpportunitiesPrices, 100); }); } // Call loadOverviewData when Overview tab is clicked const overviewTab = document.querySelector('[data-tab="overview"]'); if (overviewTab) { overviewTab.addEventListener('click', function() { setTimeout(loadOverviewData, 100); }); } // Call updateHistoricalAverages when Bar Count tab is clicked const barcountTab = document.querySelector('[data-tab="barcount"]'); if (barcountTab) { barcountTab.addEventListener('click', function() { setTimeout(updateHistoricalAverages, 100); }); } // ============================================ // OPTIONS CALCULATOR - BLACK-SCHOLES // ============================================ // Standard normal cumulative distribution function function normalCDF(x) { const a1 = 0.254829592; const a2 = -0.284496736; const a3 = 1.421413741; const a4 = -1.453152027; const a5 = 1.061405429; const p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } // Standard normal probability density function function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } // Black-Scholes option pricing function blackScholes(S, K, T, r, sigma, optionType) { // S = underlying price, K = strike, T = time to expiry (years) // r = risk-free rate, sigma = volatility, optionType = 'call' or 'put' if (T <= 0) { // At expiration if (optionType === 'call') { return Math.max(0, S - K); } else { return Math.max(0, K - S); } } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } } // Calculate Greeks function calculateGreeks(S, K, T, r, sigma, optionType) { if (T <= 0) { return { delta: optionType === 'call' ? (S > K ? 1 : 0) : (S < K ? -1 : 0), gamma: 0, theta: 0, vega: 0, rho: 0 }; } const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); // Delta let delta; if (optionType === 'call') { delta = normalCDF(d1); } else { delta = normalCDF(d1) - 1; } // Gamma (same for calls and puts) const gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); // Theta (per day) let theta; const term1 = -S * normalPDF(d1) * sigma / (2 * Math.sqrt(T)); if (optionType === 'call') { theta = term1 - r * K * Math.exp(-r * T) * normalCDF(d2); } else { theta = term1 + r * K * Math.exp(-r * T) * normalCDF(-d2); } theta = theta / 365; // Convert to per day // Vega (per 1% change in volatility) const vega = S * Math.sqrt(T) * normalPDF(d1) / 100; // Rho (per 1% change in rate) let rho; if (optionType === 'call') { rho = K * T * Math.exp(-r * T) * normalCDF(d2) / 100; } else { rho = -K * T * Math.exp(-r * T) * normalCDF(-d2) / 100; } return { delta, gamma, theta, vega, rho }; } // Main Options Calculator update function function updateOptionsCalc() { try { const S = parseFloat(document.getElementById('optCalcSymbol').value) || 607.15; const K = parseFloat(document.getElementById('optCalcStrike').value) || 610; const DTE = parseFloat(document.getElementById('optCalcDTE').value) || 7; const IV = parseFloat(document.getElementById('optCalcIV').value) || 18; const rate = parseFloat(document.getElementById('optCalcRate').value) || 5.25; const optionType = document.getElementById('optCalcType').value || 'call'; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; // Calculate option price const price = blackScholes(S, K, T, r, sigma, optionType); // Calculate Greeks const greeks = calculateGreeks(S, K, T, r, sigma, optionType); // Calculate intrinsic and time value let intrinsic; if (optionType === 'call') { intrinsic = Math.max(0, S - K); } else { intrinsic = Math.max(0, K - S); } const timeValue = Math.max(0, price - intrinsic); // Calculate break-even let breakEven; if (optionType === 'call') { breakEven = K + price; } else { breakEven = K - price; } // Update display elements const priceEl = document.getElementById('optPriceResult'); if (priceEl) priceEl.textContent = '$' + price.toFixed(2); const intrinsicEl = document.getElementById('optIntrinsicResult'); if (intrinsicEl) intrinsicEl.textContent = '$' + intrinsic.toFixed(2); const timeValueEl = document.getElementById('optTimeValueResult'); if (timeValueEl) timeValueEl.textContent = '$' + timeValue.toFixed(2); const breakEvenEl = document.getElementById('optBreakEvenResult'); if (breakEvenEl) breakEvenEl.textContent = '$' + breakEven.toFixed(2); // Update Greeks const deltaEl = document.getElementById('greekDelta'); if (deltaEl) deltaEl.textContent = greeks.delta.toFixed(3); const gammaEl = document.getElementById('greekGamma'); if (gammaEl) gammaEl.textContent = greeks.gamma.toFixed(4); const thetaEl = document.getElementById('greekTheta'); if (thetaEl) thetaEl.textContent = greeks.theta.toFixed(2); const vegaEl = document.getElementById('greekVega'); if (vegaEl) vegaEl.textContent = greeks.vega.toFixed(2); const rhoEl = document.getElementById('greekRho'); if (rhoEl) rhoEl.textContent = greeks.rho.toFixed(3); // Update P&L Scenarios updatePnLScenarios(S, K, DTE, IV, rate, optionType, price); // Update Time Decay Analysis updateThetaDecay(S, K, DTE, IV, rate, optionType, price); console.log('Options Calculator updated: ' + optionType.toUpperCase() + ' @ $' + price.toFixed(2)); } catch (e) { console.error('Error updating options calculator:', e); } } // Update P&L Scenarios table function updatePnLScenarios(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('pnlScenarios'); if (!tbody) return; const T = DTE / 365; const r = rate / 100; const sigma = IV / 100; const scenarios = [3, 2, 1, 0, -1, -2, -3]; let html = ''; scenarios.forEach(pctMove => { const newPrice = S * (1 + pctMove / 100); let payoff; if (optionType === 'call') { payoff = Math.max(0, newPrice - K); } else { payoff = Math.max(0, K - newPrice); } const pnl = (payoff - currentPrice) * 100; // Per contract const pctReturn = currentPrice > 0 ? ((payoff - currentPrice) / currentPrice * 100) : 0; const pnlColor = pnl >= 0 ? '#22c55e' : '#ef4444'; html += ` ${pctMove >= 0 ? '+' : ''}${pctMove}% $${newPrice.toFixed(2)} ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(0)} ${pctReturn >= 0 ? '+' : ''}${pctReturn.toFixed(0)}% `; }); tbody.innerHTML = html; } // Update Time Decay Analysis table function updateThetaDecay(S, K, DTE, IV, rate, optionType, currentPrice) { const tbody = document.getElementById('thetaDecay'); if (!tbody) return; const r = rate / 100; const sigma = IV / 100; const daysToShow = [DTE, Math.max(1, DTE - 2), Math.max(1, DTE - 4), 1, 0]; let html = ''; let prevPrice = currentPrice; let cumulative = 0; daysToShow.forEach((days, idx) => { const T = days / 365; let price; if (days === 0) { price = optionType === 'call' ? Math.max(0, S - K) : Math.max(0, K - S); } else { price = blackScholes(S, K, T, r, sigma, optionType); } const decay = idx === 0 ? calculateGreeks(S, K, T, r, sigma, optionType).theta : (price - prevPrice); cumulative = price - currentPrice; const dayLabel = idx === 0 ? days + ' (Today)' : (days === 0 ? '0 (Expiry)' : days.toString()); const decayStr = days === 0 ? '-' : ('$' + decay.toFixed(2)); const cumColor = cumulative < 0 ? '#ef4444' : '#22c55e'; html += ` ${dayLabel} $${price.toFixed(2)} ${decayStr} $${cumulative.toFixed(2)} `; prevPrice = price; }); tbody.innerHTML = html; } // Initialize Options Calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (typeof updateOptionsCalc === 'function') { updateOptionsCalc(); } }, 500); }); // ============================================ // TABLE SORTING UTILITIES - NEWEST FIRST // ============================================ // Auto-sort all date-based tables to show most recent first function sortAllTablesNewestFirst() { const dateTables = [ { id: 'barCountTable', dateCol: 1, isDate: true }, { id: 'historicalDataTable', dateCol: 0, isDate: true }, { id: 'tradeTableBody', dateCol: 1, isDate: true }, { id: 'reversalStatsTable', dateCol: 0, isDate: false, sortByRisk: true } ]; dateTables.forEach(config => { const tbody = document.getElementById(config.id); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll('tr')); if (rows.length === 0) return; rows.sort((a, b) => { if (config.sortByRisk) { // Sort by risk level (HIGH > MEDIUM > LOW) const riskOrder = { 'HIGH': 1, 'MEDIUM': 2, 'MED': 2, 'LOW': 3 }; const aRisk = a.querySelector('.badge')?.textContent.trim() || 'LOW'; const bRisk = b.querySelector('.badge')?.textContent.trim() || 'LOW'; return (riskOrder[aRisk] || 3) - (riskOrder[bRisk] || 3); } let aVal = a.cells[config.dateCol]?.textContent.trim() || ''; let bVal = b.cells[config.dateCol]?.textContent.trim() || ''; if (config.isDate) { // Parse dates and sort descending (newest first) const aDate = new Date(aVal); const bDate = new Date(bVal); return bDate - aDate; } else { // For non-date columns, reverse alphabetically return bVal.localeCompare(aVal); } }); rows.forEach(row => tbody.appendChild(row)); // Update row numbers after sorting updateRowNumbers(config.id); }); } // Update row numbers dynamically after sorting function updateRowNumbers(tableId) { const tbody = document.getElementById(tableId); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach((row, index) => { const firstCell = row.cells[0]; if (firstCell && !isNaN(parseInt(firstCell.textContent))) { firstCell.textContent = index + 1; } }); } // Load market stats (Today range, 52W range, Volume vs Avg) async function loadMarketStats(symbol) { if (!symbol) symbol = currentSymbol || 'SPY'; try { const response = await fetch('data/' + symbol + '.csv'); if (!response.ok) { console.log('Market stats: No data for ' + symbol); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse all rows to calculate 52W high/low and volume average let high52W = -Infinity; let low52W = Infinity; let volumeSum = 0; let volumeCount = 0; // Get last 252 trading days (approximately 1 year) for 52W calculations const startIdx = Math.max(1, lines.length - 252); for (let i = startIdx; i < lines.length; i++) { const cols = lines[i].split(','); const high = parseFloat(cols[2]); const low = parseFloat(cols[3]); const volume = parseFloat(cols[5]); if (!isNaN(high) && high > high52W) high52W = high; if (!isNaN(low) && low < low52W) low52W = low; // Calculate 20-day volume average if (i >= lines.length - 20 && !isNaN(volume)) { volumeSum += volume; volumeCount++; } } const avgVolume = volumeCount > 0 ? volumeSum / volumeCount : 0; // Get today's data (last row) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const todayHigh = parseFloat(cols[2]); const todayLow = parseFloat(cols[3]); const todayClose = parseFloat(cols[4]); const todayVolume = parseFloat(cols[5]); // Update Today Low/High if (!isNaN(todayLow)) { var el=document.getElementById('statTodayLow');if(el)el.textContent = 'L: $' + todayLow.toFixed(2); } if (!isNaN(todayHigh)) { var el=document.getElementById('statTodayHigh');if(el)el.textContent = 'H: $' + todayHigh.toFixed(2); } // Update 52W Range with color coding const stat52W = document.getElementById('stat52WRange'); if (stat52W && !isNaN(high52W) && !isNaN(low52W)) { stat52W.textContent = '$' + low52W.toFixed(2) + ' - $' + high52W.toFixed(2); // Color based on where current price is in 52W range const range = high52W - low52W; const position = (todayClose - low52W) / range; if (position > 0.8) { stat52W.style.color = '#22c55e'; // Near 52W high - green } else if (position < 0.2) { stat52W.style.color = '#ef4444'; // Near 52W low - red } else { stat52W.style.color = 'var(--text)'; // Neutral } } // Update Volume vs Avg const statVolume = document.getElementById('statVolumeVsAvg'); if (statVolume && avgVolume > 0 && !isNaN(todayVolume)) { const volPct = ((todayVolume - avgVolume) / avgVolume) * 100; const volSign = volPct >= 0 ? '+' : ''; statVolume.textContent = volSign + volPct.toFixed(0) + '% vs Avg'; // Color based on volume if (volPct > 20) { statVolume.style.color = '#22c55e'; // High volume - green } else if (volPct < -30) { statVolume.style.color = '#ef4444'; // Low volume - red } else { statVolume.style.color = 'var(--text)'; // Normal } } } catch (error) { console.log('Error loading market stats:', error); } } // Call market stats on page load document.addEventListener('DOMContentLoaded', function() { loadMarketStats(); }); // ==================== TIMESTAMP FUNCTIONS ==================== // Format timestamp as "Dec 2, 2025 2:45 PM ET" or short "2:45 PM" function formatLastUpdated(date, short = false) { if (!date) return '--'; const d = new Date(date); if (isNaN(d.getTime())) return '--'; const options = short ? { hour: 'numeric', minute: '2-digit', hour12: true } : { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }; let formatted = d.toLocaleString('en-US', options); if (!short) formatted += ' ET'; return formatted; } // Get color based on staleness function getTimestampColor(lastUpdateTime) { if (!lastUpdateTime) return 'var(--muted)'; const now = new Date(); const updated = new Date(lastUpdateTime); const diffMinutes = (now - updated) / (1000 * 60); // Check if data is from previous day if (updated.toDateString() !== now.toDateString()) { return '#ef4444'; // Red - previous day } if (diffMinutes <= 30) return '#22c55e'; // Green - within 30 min if (diffMinutes <= 60) return '#eab308'; // Yellow - 30-60 min if (diffMinutes <= 240) return '#f97316'; // Orange - 1-4 hours return '#ef4444'; // Red - more than 4 hours } // Update all timestamp displays function updateAllTimestamps() { const now = new Date(); const timestamps = { global: now, analysis: now, health: now, multiCharts: now, trades: now }; // Save to localStorage localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); // Update displays updateTimestampDisplays(timestamps); } // Update specific section timestamp function updateSectionTimestamp(section) { const timestamps = JSON.parse(localStorage.getItem('stockAgentTimestamps') || '{}'); timestamps[section] = new Date().toISOString(); localStorage.setItem('stockAgentTimestamps', JSON.stringify(timestamps)); updateTimestampDisplays(timestamps); } // Update timestamp display elements function updateTimestampDisplays(timestamps) { // Global timestamp const globalEl = document.getElementById('globalLastUpdated'); if (globalEl && timestamps.global) { globalEl.textContent = formatLastUpdated(timestamps.global, true); globalEl.style.color = getTimestampColor(timestamps.global); globalEl.title = 'Last updated: ' + formatLastUpdated(timestamps.global); } // Analysis timestamp const analysisEl = document.getElementById('analysisLastUpdated'); if (analysisEl && timestamps.analysis) { analysisEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.analysis, true); analysisEl.style.color = getTimestampColor(timestamps.analysis); analysisEl.title = 'Last updated: ' + formatLastUpdated(timestamps.analysis); } // Health timestamp const healthEl = document.getElementById('healthLastUpdated'); if (healthEl && timestamps.health) { healthEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.health, true); healthEl.style.color = getTimestampColor(timestamps.health); healthEl.title = 'Last health check: ' + formatLastUpdated(timestamps.health); } // Multi-Charts timestamp const multiChartsEl = document.getElementById('multiChartsLastUpdated'); if (multiChartsEl && timestamps.multiCharts) { multiChartsEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.multiCharts, true); multiChartsEl.style.color = getTimestampColor(timestamps.multiCharts); multiChartsEl.title = 'Last updated: ' + formatLastUpdated(timestamps.multiCharts); } // Trades timestamp const tradesEl = document.getElementById('tradesLastUpdated'); if (tradesEl && timestamps.trades) { tradesEl.textContent = '⏱️ ' + formatLastUpdated(timestamps.trades, true); tradesEl.style.color = getTimestampColor(timestamps.trades); tradesEl.title = 'Last updated: ' + formatLastUpdated(timestamps.trades); } } // Load saved timestamps from localStorage on page load function loadSavedTimestamps() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { const timestamps = JSON.parse(saved); updateTimestampDisplays(timestamps); } catch (e) { console.log('Error loading timestamps:', e); } } } // Check staleness and update colors every minute setInterval(function() { const saved = localStorage.getItem('stockAgentTimestamps'); if (saved) { try { updateTimestampDisplays(JSON.parse(saved)); } catch (e) {} } }, 60000); // Check every minute // Load timestamps on page load document.addEventListener('DOMContentLoaded', loadSavedTimestamps); // ==================== END TIMESTAMP FUNCTIONS ==================== function setTradingMode(mode) { tradingMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector(`.mode-btn.${mode}`).classList.add('active'); var el=document.getElementById('symbolBanner');if(el)el.className = `symbol-banner ${mode}-mode`; var el=document.getElementById('currentModeDisplay');if(el)el.textContent = mode.toUpperCase(); var el=document.getElementById('aiContextMode');if(el)el.textContent = mode.toUpperCase(); } async function changeSymbol() { const newSymbol = document.getElementById('symbolSelect').value; console.log(`🔄 Changing symbol from ${currentSymbol} to ${newSymbol}`); currentSymbol = newSymbol; // Update ALL symbol displays immediately const symbolDisplays = ['currentSymbolDisplay', 'tickerSymbol', 'aiContextSymbol', 'barCountCurrentSymbol', 'gannCurrentSymbol', 'patternSymbol', 'historicalSymbol']; symbolDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = currentSymbol; }); const nameDisplays = ['currentSymbolName', 'tickerName']; nameDisplays.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; }); // Update Multi-Charts symbol selector to match const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) multiChartSymbol.value = currentSymbol; // Update Daily Analysis symbol display const confluenceSymbol = document.getElementById('confluenceSymbol'); if (confluenceSymbol) confluenceSymbol.textContent = currentSymbol + ' Analysis'; // Show loading notification showNotification('Loading ' + currentSymbol + ' data...', 'info'); // Load the new symbol's CSV data and update everything await loadAllData(); // Update Overview tab with CSV data if (typeof loadOverviewData === 'function') loadOverviewData(); // Update market stats for new symbol if (typeof loadMarketStats === 'function') loadMarketStats(currentSymbol); // Update Options Calculator with new symbol if (typeof updateOptionsCalcForSymbol === 'function') updateOptionsCalcForSymbol(); // === UPDATE ALL GANN SECTIONS === if (typeof updateGannAnalysisForSymbol === 'function') updateGannAnalysisForSymbol(currentSymbol); if (typeof updateGannForecast === 'function') updateGannForecast(currentSymbol); if (typeof updateSquareOf9 === 'function') updateSquareOf9(currentSymbol); if (typeof updateMasterConfluence === 'function') updateMasterConfluence(currentSymbol); if (typeof updatePriceMathRelationships === 'function') updatePriceMathRelationships(currentSymbol); if (typeof updateFibonacciLevelsSafe === 'function') updateFibonacciLevelsSafe(currentSymbol); if (typeof updateSacredNumbers === 'function') updateSacredNumbers(currentSymbol); if (typeof updateGannAngles === 'function') updateGannAngles(currentSymbol); if (typeof updateGannTimeCycles === 'function') updateGannTimeCycles(currentSymbol); // === UPDATE PATTERN & HISTORICAL SECTIONS === if (typeof updateDataPatternDetection === 'function') updateDataPatternDetection(); if (typeof updatePatternDetection === 'function') updatePatternDetection(currentSymbol); if (typeof updatePatternDetectionSummary === 'function') updatePatternDetectionSummary(); if (typeof updatePatternTableFromCSV === 'function') updatePatternTableFromCSV(); if (typeof updateHistoricalData === 'function') updateHistoricalData(currentSymbol); if (typeof updateHistoricalDataTable === 'function') updateHistoricalDataTable(); if (typeof updateTradeStatisticsSafe === 'function') updateTradeStatisticsSafe(currentSymbol); if (typeof updateTradeStatistics === 'function') updateTradeStatistics(); // === UPDATE BAR COUNT SECTIONS === if (typeof updateBarCountAnalysis === 'function') updateBarCountAnalysis(); if (typeof updateBarCount === 'function') updateBarCount(currentSymbol); // === UPDATE OTHER SECTIONS === if (typeof updateOverviewForSymbol === 'function') updateOverviewForSymbol(currentSymbol); if (typeof updateSignalsForSymbol === 'function') updateSignalsForSymbol(currentSymbol); if (typeof updateDailyAnalysisForSymbol === 'function') updateDailyAnalysisForSymbol(currentSymbol); if (typeof updateHeaderForSymbol === 'function') updateHeaderForSymbol(currentSymbol); // Update Gann Analysis tab if active const gannTab = document.getElementById('gann-analysis'); if (gannTab && gannTab.classList.contains('active')) { if (typeof updateGannAnalysis === 'function') updateGannAnalysis(); } console.log('✅ All sections updated for ' + currentSymbol); showNotification(currentSymbol + ' loaded!', 'success'); } function parseCSV(text) { const lines = text.trim().split('\n'); if (lines.length < 2) return []; const headers = lines[0].split(',').map(h => h.trim()); return lines.slice(1).map(line => { const values = line.split(','); const obj = {}; headers.forEach((h, i) => obj[h] = values[i]?.trim() || ''); return obj; }); } async function loadAllData() { var el=document.getElementById('loadingOverlay');if(el)el.classList.add('active'); try { // Try multiple paths for the CSV file let r = null; let loadedSymbol = currentSymbol; // Try data/ folder first, then root const paths = [ `data/${currentSymbol}.csv?t=${Date.now()}`, `${currentSymbol}.csv?t=${Date.now()}` ]; for (const path of paths) { r = await fetch(path).catch(() => null); if (r?.ok) { console.log(`✅ Found data at: ${path}`); break; } } // If the selected symbol's CSV doesn't exist, fall back to SPY if (!r?.ok) { console.warn(`${currentSymbol}.csv not found, falling back to SPY`); // Try SPY in both locations r = await fetch('data/SPY.csv?t=' + Date.now()).catch(() => null); loadedSymbol = 'SPY'; // Update the symbol selector back to SPY if data not available const select = document.getElementById('symbolSelect'); if (select) select.value = 'SPY'; currentSymbol = 'SPY'; // Show notification showDataNotification(`Data loaded for SPY`); } if (r?.ok) { currentData = parseCSV(await r.text()); if (currentData.length) { console.log(`✅ Loaded ${currentData.length} bars for ${currentSymbol}`); // Update ALL dashboard components updateDashboard(); updateBarCountAnalysis(); updateReversalStats(); updatePriceChart(); updateOverviewAiAnalysis(); updateDataPatternDetection(); updatePatternTableFromCSV(); updateOverviewTab(); // Update Daily Analysis if that tab is active const analysisTab = document.getElementById('daily-analysis'); if (analysisTab && analysisTab.classList.contains('active')) { await loadRealAgentSignals(); generateDailyReport(); } // Update Multi-Charts if that tab is active const multiChartsTab = document.getElementById('multi-charts'); if (multiChartsTab && multiChartsTab.classList.contains('active')) { updateAllMultiCharts(); } // Update symbol displays var el=document.getElementById('currentSymbolDisplay');if(el)el.textContent = currentSymbol; var el=document.getElementById('currentSymbolName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('tickerSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('tickerName');if(el)el.textContent = SYMBOL_NAMES[currentSymbol] || currentSymbol; var el=document.getElementById('confluenceSymbol');if(el)el.textContent = currentSymbol + ' Analysis'; // Update market stats in header loadMarketStats(currentSymbol); // Update timestamps updateAllTimestamps(); // Load all symbols data for AI and cross-symbol analysis await loadAllSymbolsData(); } } } catch (e) { console.error('Error:', e); } var el=document.getElementById('loadingOverlay');if(el)el.classList.remove('active'); } function showDataNotification(message) { // Create a temporary notification const notif = document.createElement('div'); notif.style.cssText = 'position:fixed;top:80px;right:20px;background:var(--warning);color:white;padding:12px 20px;border-radius:8px;font-size:12px;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.2);'; notif.textContent = '⚠️ ' + message; document.body.appendChild(notif); setTimeout(() => notif.remove(), 3000); } async function loadAllSymbolsData() { for (const symbol of SYMBOLS) { try { const r = await fetch(`data/${symbol}.csv?t=${Date.now()}`).catch(() => null); if (r?.ok) { allSymbolsData[symbol] = parseCSV(await r.text()); } } catch (e) { console.error(`Error loading ${symbol}:`, e); } } } function updateDashboard() { if (!currentData || !currentData.length) { console.warn("updateDashboard: No data"); return; } const lat = currentData[currentData.length - 1]; const prev = currentData[currentData.length - 2] || lat; const price = parseFloat(lat.Close) || 0; const prevPrice = parseFloat(prev.Close) || price; const chg = price - prevPrice; const pct = prevPrice ? (chg / prevPrice * 100) : 0; const atr = parseFloat(lat.ATR) || 5; const bias = (lat.Bias || '').toUpperCase(); // Header ticker var el=document.getElementById('tickerPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('tickerChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('tickerChange');if(el)el.className = 'ticker-change ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('tickerPct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; // Metrics var el=document.getElementById('currentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('priceSource');if(el)el.textContent = `${currentData.length} bars`; var el=document.getElementById('dailyChange');if(el)el.textContent = (chg >= 0 ? '+' : '') + '$' + Math.abs(chg).toFixed(2); el=document.getElementById('dailyChange');if(el)el.className = 'metric-value ' + (chg >= 0 ? 'positive' : 'negative'); var el=document.getElementById('changePct');if(el)el.textContent = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%'; var el=document.getElementById('volumeDisplay');if(el)el.textContent = ((parseFloat(lat.Volume) || 0) / 1e6).toFixed(1) + 'M'; var el=document.getElementById('signalStatus');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('signalConf');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; var el=document.getElementById('atrDisplay');if(el)el.textContent = '$' + atr.toFixed(2); // Date/Time const dateStr = lat.Date || ''; const [datePart, timePart] = dateStr.includes('T') ? dateStr.split('T') : [dateStr, 'EOD']; var el=document.getElementById('dataDate');if(el)el.textContent = datePart; var el=document.getElementById('dataTime');if(el)el.textContent = timePart.replace('Z', ''); // AI Context var el=document.getElementById('aiContextPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('aiContextSignal');if(el)el.textContent = bias || 'HOLD'; var el=document.getElementById('aiContextConsensus');if(el)el.textContent = `${lat.PriceConfluence || 0}/4`; // Signals const ip = TRADING_PARAMS.intraday; const sp = TRADING_PARAMS.swing; var el=document.getElementById('intradaySignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('intradaySignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('intradayEntry');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('intradayStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * ip.stopATR) : (price - atr * ip.stopATR)).toFixed(2); var el=document.getElementById('intradayT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target1ATR) : (price + atr * ip.target1ATR)).toFixed(2); var el=document.getElementById('intradayT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * ip.target2ATR) : (price + atr * ip.target2ATR)).toFixed(2); var el=document.getElementById('intradayStrike');if(el)el.textContent = '$' + Math.round(price) + ' ' + (bias || 'CALL'); var el=document.getElementById('swingSignal');if(el)el.textContent = bias || 'HOLD'; el=document.getElementById('swingSignal');if(el)el.className = `badge badge-${bias === 'CALL' ? 'success' : (bias === 'PUT' ? 'danger' : 'warning')}`; var el=document.getElementById('swingEntry');if(el)el.textContent = '$' + (price - 2).toFixed(0) + '-' + (price + 2).toFixed(0); var el=document.getElementById('swingStop');if(el)el.textContent = '$' + (bias === 'PUT' ? (price + atr * sp.stopATR) : (price - atr * sp.stopATR)).toFixed(2); var el=document.getElementById('swingT1');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target1ATR) : (price + atr * sp.target1ATR)).toFixed(2); var el=document.getElementById('swingT2');if(el)el.textContent = '$' + (bias === 'PUT' ? (price - atr * sp.target2ATR) : (price + atr * sp.target2ATR)).toFixed(2); var el=document.getElementById('swingStrike');if(el)el.textContent = '$' + (Math.round(price/5)*5) + ' ' + (bias || 'CALL'); updateReferenceData(); updateFibonacci(); updateSignalsFibonacci(); } function updateReferenceData() { const lat = currentData[currentData.length - 1]; const yearData = currentData.slice(-252); const highs = yearData.map(d => parseFloat(d.High) || 0); const lows = yearData.map(d => parseFloat(d.Low) || 0); var el=document.getElementById('week52High');if(el)el.textContent = '$' + Math.max(...highs).toFixed(2); var el=document.getElementById('week52Low');if(el)el.textContent = '$' + Math.min(...lows).toFixed(2); var el=document.getElementById('todayHigh');if(el)el.textContent = '$' + (parseFloat(lat.High) || 0).toFixed(2); var el=document.getElementById('todayLow');if(el)el.textContent = '$' + (parseFloat(lat.Low) || 0).toFixed(2); const todayVol = parseFloat(lat.Volume) || 0; const volumes20 = currentData.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVol = volumes20.reduce((a, b) => a + b, 0) / 20; var el=document.getElementById('avgVolume20');if(el)el.textContent = (avgVol / 1e6).toFixed(1) + 'M'; var el=document.getElementById('volumeVsAvg');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '%'; var el=document.getElementById('volumeVsAvgSmall');if(el)el.textContent = ((todayVol/avgVol - 1) * 100).toFixed(0) + '% vs avg'; } function updateFibonacci() { const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const price = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; const fib382 = swingHigh - (range * 0.382); const fib500 = swingHigh - (range * 0.500); const fib618 = swingHigh - (range * 0.618); const fib100 = swingHigh; const fib1272 = swingLow + (range * 1.272); const fib1618 = swingLow + (range * 1.618); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('fibS3');if(el)el.textContent = '$' + fib618.toFixed(2); var el=document.getElementById('chartR1');if(el)el.textContent = '$' + fib100.toFixed(2); var el=document.getElementById('chartR2');if(el)el.textContent = '$' + fib1272.toFixed(2); var el=document.getElementById('chartR3');if(el)el.textContent = '$' + fib1618.toFixed(2); var el=document.getElementById('chartS1');if(el)el.textContent = '$' + fib382.toFixed(2); var el=document.getElementById('chartS2');if(el)el.textContent = '$' + fib500.toFixed(2); var el=document.getElementById('chartS3');if(el)el.textContent = '$' + fib618.toFixed(2); let zone = 'NEUTRAL'; if (price > fib100) zone = 'BREAKOUT'; else if (price > fib382) zone = 'STRONG'; else if (price > fib500) zone = 'PULLBACK'; else zone = 'DEEP'; var el=document.getElementById('fibZone');if(el)el.textContent = zone; const fastSMA = parseFloat(currentData[currentData.length-1].FastSMA) || price; const slowSMA = parseFloat(currentData[currentData.length-1].SlowSMA) || price; const trend = fastSMA > slowSMA ? 'BULLISH' : 'BEARISH'; var el=document.getElementById('trendBadge');if(el)el.textContent = trend; el=document.getElementById('trendBadge');if(el)el.className = `badge badge-${trend === 'BULLISH' ? 'success' : 'danger'}`; window.fibLevels = { fib100, fib1272, fib1618, fib382, fib500, fib618, swingHigh, swingLow }; } // FIXED: Bar Count now uses currentSymbol - NEWEST FIRST function updateBarCountAnalysis() { const barCount = parseInt(document.getElementById('barCountSelect')?.value || 20); // Use current symbol's data const symbolData = currentData; if (!symbolData || symbolData.length < 1) return; const actualBars = Math.min(barCount, symbolData.length); const data = symbolData.slice(-actualBars); const table = document.getElementById('barCountTable'); if (!table) return; // Update the symbol display var el=document.getElementById('barCountCurrentSymbol');if(el)el.textContent = currentSymbol; // Build rows array (oldest to newest for correct bar count calculation) let rows = []; let baseCount = 0, gannCount = 0, dqnCount = 0, waveCount = 0; data.forEach((d, i) => { const o = parseFloat(d.Open) || 0; const c = parseFloat(d.Close) || 0; const isBullish = c >= o; if (isBullish) { baseCount = baseCount >= 0 ? baseCount + 1 : 1; gannCount = gannCount >= 0 ? gannCount + 1 : 1; dqnCount = dqnCount >= 0 ? dqnCount + 1 : 1; waveCount = waveCount >= 0 ? waveCount + 1 : 1; } else { baseCount = baseCount <= 0 ? baseCount - 1 : -1; gannCount = gannCount <= 0 ? gannCount - 1 : -1; dqnCount = dqnCount <= 0 ? dqnCount - 1 : -1; waveCount = waveCount <= 0 ? waveCount - 1 : -1; } const avg = (baseCount + gannCount + dqnCount + waveCount) / 4; const date = (d.Date || '').split('T')[0].slice(5); rows.push({ date, o, high: parseFloat(d.High)||0, low: parseFloat(d.Low)||0, c, isBullish, baseCount, gannCount, dqnCount, waveCount, avg }); }); // Reverse to show newest first, then build HTML with correct row numbers rows.reverse(); let html = ''; rows.forEach((r, i) => { html += ` ${i + 1} ${r.date} ${r.o.toFixed(0)}/${r.high.toFixed(0)}/${r.low.toFixed(0)}/${r.c.toFixed(0)} ${r.isBullish ? '📈' : '📉'} ${r.baseCount > 0 ? '+' : ''}${r.baseCount} ${r.gannCount > 0 ? '+' : ''}${r.gannCount} ${r.dqnCount > 0 ? '+' : ''}${r.dqnCount} ${r.waveCount > 0 ? '+' : ''}${r.waveCount} ${r.avg.toFixed(1)} `; }); table.innerHTML = html; var el=document.getElementById('baseBarCount');if(el)el.textContent = (baseCount > 0 ? '+' : '') + baseCount; document.getElementById('baseBarCount').style.color = baseCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('gannBarCount');if(el)el.textContent = (gannCount > 0 ? '+' : '') + gannCount; document.getElementById('gannBarCount').style.color = gannCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('dqnBarCount');if(el)el.textContent = (dqnCount > 0 ? '+' : '') + dqnCount; document.getElementById('dqnBarCount').style.color = dqnCount > 0 ? 'var(--success)' : 'var(--danger)'; var el=document.getElementById('waveBarCount');if(el)el.textContent = (waveCount > 0 ? '+' : '') + waveCount; document.getElementById('waveBarCount').style.color = waveCount > 0 ? 'var(--success)' : 'var(--danger)'; // Also update historical averages when bar count analysis updates updateHistoricalAverages(); } // Calculate and update Historical Averages for all symbols async function updateHistoricalAverages() { const symbols = ['SPY', 'QQQ', 'IWM', 'XLF', 'XLE', 'XLK']; for (const symbol of symbols) { try { // Try to get data from allSymbolsData or fetch it let data = allSymbolsData[symbol]; if (!data || data.length < 20) { // Try to fetch the CSV let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (response?.ok) { const text = await response.text(); data = parseCSV(text); } } if (!data || data.length < 20) continue; // Get last 20 bars const last20 = data.slice(-20); // Calculate O→H, O→L, O→C, Range for each bar let sumOH = 0, sumOL = 0, sumOC = 0, sumRange = 0; let maxOH = 0, maxOL = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const oh = high - open; // Open to High (always positive) const ol = open - low; // Open to Low (always positive) const oc = close - open; // Open to Close (can be negative) const range = high - low; sumOH += oh; sumOL += ol; sumOC += oc; sumRange += range; if (oh > maxOH) maxOH = oh; if (ol > maxOL) maxOL = ol; }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgOC = sumOC / 20; const avgRange = sumRange / 20; const callTarget = avgOH * 0.85; const putTarget = avgOL * 0.85; // Update table cells const symLower = symbol.toLowerCase(); const updateEl = (id, value, prefix = '') => { const el = document.getElementById(id); if (el) el.textContent = prefix + '$' + value.toFixed(2); }; updateEl(symLower + 'AvgOH', avgOH, '+'); updateEl(symLower + 'MaxOH', maxOH, '+'); updateEl(symLower + 'AvgOL', avgOL, '-'); updateEl(symLower + 'MaxOL', maxOL, '-'); updateEl(symLower + 'AvgOC', Math.abs(avgOC), avgOC >= 0 ? '+' : '-'); updateEl(symLower + 'AvgRange', avgRange); updateEl(symLower + 'CallTarget', callTarget, '+'); updateEl(symLower + 'PutTarget', putTarget, '-'); } catch (e) { console.log('Error calculating averages for ' + symbol + ':', e); } } // Update Today vs Average section for current symbol updateTodayVsAverage(); } // Update Today vs Average comparison for current symbol function updateTodayVsAverage() { if (!currentData || currentData.length < 21) return; const last20 = currentData.slice(-21, -1); // Previous 20 bars (not including today) const today = currentData[currentData.length - 1]; // Calculate averages from previous 20 bars let sumOH = 0, sumOL = 0, sumRange = 0; last20.forEach(bar => { const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; sumOH += (high - open); sumOL += (open - low); sumRange += (high - low); }); const avgOH = sumOH / 20; const avgOL = sumOL / 20; const avgRange = sumRange / 20; // Today's values const todayOpen = parseFloat(today.Open) || 0; const todayHigh = parseFloat(today.High) || 0; const todayLow = parseFloat(today.Low) || 0; const todayOH = todayHigh - todayOpen; const todayOL = todayOpen - todayLow; const todayRange = todayHigh - todayLow; // Calculate percentages const ohPct = avgOH > 0 ? (todayOH / avgOH) * 100 : 0; const olPct = avgOL > 0 ? (todayOL / avgOL) * 100 : 0; const rangePct = avgRange > 0 ? (todayRange / avgRange) * 100 : 0; // Update O→H box const ohPctEl = document.getElementById('todayOHpct'); const ohDetailEl = document.getElementById('todayOHdetail'); const ohStatusEl = document.getElementById('todayOHstatus'); const ohBoxEl = document.getElementById('todayOHvsAvgBox'); if (ohPctEl) ohPctEl.textContent = ohPct.toFixed(0) + '%'; if (ohDetailEl) ohDetailEl.textContent = '$' + todayOH.toFixed(2) + ' of $' + avgOH.toFixed(2) + ' avg'; if (ohStatusEl && ohBoxEl) { if (ohPct < 50) { ohBoxEl.style.background = 'rgba(34,197,94,0.1)'; ohStatusEl.style.color = '#22c55e'; ohStatusEl.textContent = 'Room to run - upside potential'; } else if (ohPct < 85) { ohBoxEl.style.background = 'rgba(234,179,8,0.1)'; ohStatusEl.style.color = '#eab308'; ohStatusEl.textContent = 'Approaching avg - watch closely'; } else if (ohPct < 100) { ohBoxEl.style.background = 'rgba(249,115,22,0.1)'; ohStatusEl.style.color = '#f97316'; ohStatusEl.textContent = 'Nearing avg - consider taking profits'; } else { ohBoxEl.style.background = 'rgba(239,68,68,0.1)'; ohStatusEl.style.color = '#ef4444'; ohStatusEl.textContent = 'Exceeded avg - extended move'; } } // Update O→L box const olPctEl = document.getElementById('todayOLpct'); const olDetailEl = document.getElementById('todayOLdetail'); const olStatusEl = document.getElementById('todayOLstatus'); const olBoxEl = document.getElementById('todayOLvsAvgBox'); if (olPctEl) olPctEl.textContent = olPct.toFixed(0) + '%'; if (olDetailEl) olDetailEl.textContent = '$' + todayOL.toFixed(2) + ' of $' + avgOL.toFixed(2) + ' avg'; if (olStatusEl && olBoxEl) { if (olPct < 50) { olBoxEl.style.background = 'rgba(34,197,94,0.1)'; olStatusEl.style.color = '#22c55e'; olStatusEl.textContent = 'Room to fall - downside potential'; } else if (olPct < 85) { olBoxEl.style.background = 'rgba(234,179,8,0.1)'; olStatusEl.style.color = '#eab308'; olStatusEl.textContent = 'Approaching avg - watch closely'; } else if (olPct < 100) { olBoxEl.style.background = 'rgba(249,115,22,0.1)'; olStatusEl.style.color = '#f97316'; olStatusEl.textContent = 'Nearing avg - consider covering puts'; } else { olBoxEl.style.background = 'rgba(239,68,68,0.1)'; olStatusEl.style.color = '#ef4444'; olStatusEl.textContent = 'Exceeded avg - strong selling pressure'; } } // Update Range box const rangePctEl = document.getElementById('todayRangePct'); const rangeDetailEl = document.getElementById('todayRangeDetail'); const rangeStatusEl = document.getElementById('todayRangeStatus'); const rangeBoxEl = document.getElementById('todayRangevsAvgBox'); if (rangePctEl) rangePctEl.textContent = rangePct.toFixed(0) + '%'; if (rangeDetailEl) rangeDetailEl.textContent = '$' + todayRange.toFixed(2) + ' of $' + avgRange.toFixed(2) + ' avg'; if (rangeStatusEl && rangeBoxEl) { if (rangePct < 50) { rangeBoxEl.style.background = 'rgba(59,130,246,0.1)'; rangeStatusEl.style.color = '#3b82f6'; rangeStatusEl.textContent = 'Low volatility - quiet day'; } else if (rangePct < 85) { rangeBoxEl.style.background = 'rgba(34,197,94,0.1)'; rangeStatusEl.style.color = '#22c55e'; rangeStatusEl.textContent = 'Normal volatility day'; } else if (rangePct < 120) { rangeBoxEl.style.background = 'rgba(234,179,8,0.1)'; rangeStatusEl.style.color = '#eab308'; rangeStatusEl.textContent = 'Above average volatility'; } else { rangeBoxEl.style.background = 'rgba(239,68,68,0.1)'; rangeStatusEl.style.color = '#ef4444'; rangeStatusEl.textContent = 'High volatility - be cautious'; } } } // Update Signals Tab Fibonacci Table with dynamic data function updateSignalsFibonacci() { if (!currentData || currentData.length < 60) return; const tbody = document.getElementById('sigFibTable'); const proximityEl = document.getElementById('sigFibProximity'); const symbolEl = document.getElementById('sigFibSymbol'); if (!tbody) return; // Update symbol display if (symbolEl) symbolEl.textContent = currentSymbol; // Calculate Fibonacci levels from recent data const recent = currentData.slice(-60); const highs = recent.map(d => parseFloat(d.High) || 0); const lows = recent.map(d => parseFloat(d.Low) || 0); const swingHigh = Math.max(...highs); const swingLow = Math.min(...lows); const currentPrice = parseFloat(currentData[currentData.length - 1].Close) || 0; const range = swingHigh - swingLow; // Find swing high/low dates const swingHighIdx = highs.indexOf(swingHigh); const swingLowIdx = lows.indexOf(swingLow); const swingHighDate = recent[swingHighIdx]?.Date ? new Date(recent[swingHighIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; const swingLowDate = recent[swingLowIdx]?.Date ? new Date(recent[swingLowIdx].Date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '--'; // Fibonacci levels with implications const levels = [ { name: 'R3 (161.8%)', price: swingLow + (range * 1.618), type: 'resistance', tf: 'Weekly', implication: 'Extended - Take Profits' }, { name: 'R2 (127.2%)', price: swingLow + (range * 1.272), type: 'resistance', tf: 'Daily', implication: 'Strong Resistance' }, { name: 'R1 (100%)', price: swingHigh, type: 'resistance', tf: 'Daily', implication: 'First Target', dateSet: swingHighDate }, { name: 'PIVOT', price: (swingHigh + swingLow) / 2, type: 'pivot', tf: 'Daily', implication: 'Key Decision Level' }, { name: 'S1 (38.2%)', price: swingHigh - (range * 0.382), type: 'support', tf: 'Daily', implication: 'First Support' }, { name: 'S2 (50.0%)', price: swingHigh - (range * 0.500), type: 'support', tf: 'Daily', implication: 'Key Support - Add Zone' }, { name: 'S3 (61.8%)', price: swingHigh - (range * 0.618), type: 'support', tf: 'Weekly', implication: 'Deep Support - Reversal', dateSet: swingLowDate } ]; // Find closest level let closestLevel = null; let closestDistance = Infinity; levels.forEach(level => { const dist = Math.abs(currentPrice - level.price); if (dist < closestDistance) { closestDistance = dist; closestLevel = level; } }); const closestPct = closestLevel ? ((closestLevel.price - currentPrice) / currentPrice * 100).toFixed(1) : 0; // Update proximity alert if (proximityEl && closestLevel) { const direction = closestLevel.price > currentPrice ? 'below' : 'above'; const levelType = closestLevel.type === 'resistance' ? '🔴' : closestLevel.type === 'support' ? '🟢' : '🔵'; proximityEl.innerHTML = `${levelType} ${currentSymbol} is ${Math.abs(closestPct)}% away from ${closestLevel.name} ($${closestLevel.price.toFixed(2)})`; proximityEl.style.color = Math.abs(parseFloat(closestPct)) < 1 ? '#ef4444' : 'inherit'; } // Generate table rows let html = ''; levels.forEach(level => { const distance = ((level.price - currentPrice) / currentPrice * 100); const distanceStr = distance > 0 ? `+${distance.toFixed(1)}%` : `${distance.toFixed(1)}%`; const isClosest = level === closestLevel; const isApproaching = isClosest && Math.abs(distance) < 2; let rowStyle = ''; let rowColor = ''; if (level.type === 'resistance') { rowColor = '#22c55e'; if (isApproaching) rowStyle = 'background: rgba(34,197,94,0.15);'; } else if (level.type === 'support') { rowColor = '#ef4444'; if (isApproaching) rowStyle = 'background: rgba(239,68,68,0.15);'; } else { rowStyle = 'background: rgba(59,130,246,0.1);'; rowColor = '#3b82f6'; } const approaching = isApproaching ? 'YES' : 'NO'; const dateSet = level.dateSet || '--'; html += ` ${isClosest ? '🎯 ' : ''}${level.name} $${level.price.toFixed(2)} ${level.type === 'pivot' ? 'Current' : distanceStr} ${approaching} ${dateSet} ${level.tf} ${level.implication} `; }); tbody.innerHTML = html; } function updateReversalStats() { const table = document.getElementById('reversalStatsTable'); if (!table) return; let rows = []; // First, always show current symbol const symbolsToShow = [currentSymbol]; // Add other symbols if data is available for (const symbol of SYMBOLS) { if (symbol !== currentSymbol && allSymbolsData[symbol] && allSymbolsData[symbol].length >= 10) { symbolsToShow.push(symbol); } } for (const symbol of symbolsToShow) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 10) continue; let bullishRuns = [], bearishRuns = [], currentRun = 0, lastDir = null; for (const d of data) { const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentRun = 1; } else if (isBullish === lastDir) { currentRun++; } else { if (lastDir) bullishRuns.push(currentRun); else bearishRuns.push(currentRun); lastDir = isBullish; currentRun = 1; } } const avgBullish = bullishRuns.length ? (bullishRuns.reduce((a,b) => a+b, 0) / bullishRuns.length).toFixed(1) : 'N/A'; const avgBearish = bearishRuns.length ? (bearishRuns.reduce((a,b) => a+b, 0) / bearishRuns.length).toFixed(1) : 'N/A'; const currentDir = lastDir ? 'Bullish' : 'Bearish'; const avgForDir = lastDir ? parseFloat(avgBullish) : parseFloat(avgBearish); const pctOfAvg = avgForDir ? ((currentRun / avgForDir) * 100).toFixed(0) : 'N/A'; let risk = 'LOW', riskClass = 'success', riskOrder = 3; if (pctOfAvg !== 'N/A') { const pct = parseInt(pctOfAvg); if (pct > 150) { risk = 'HIGH'; riskClass = 'danger'; riskOrder = 1; } else if (pct > 100) { risk = 'MEDIUM'; riskClass = 'warning'; riskOrder = 2; } } rows.push({ symbol, avgBullish, avgBearish, currentRun, currentDir, pctOfAvg, risk, riskClass, riskOrder, isCurrentSymbol: symbol === currentSymbol }); } // Sort by risk (HIGH first, then MEDIUM, then LOW) rows.sort((a, b) => a.riskOrder - b.riskOrder); let html = ''; if (rows.length === 0) { html = 'Loading data... Select a symbol and wait for data to load.'; } else { rows.forEach(r => { html += ` ${r.isCurrentSymbol ? '➤ ' : ''}${r.symbol} ${r.avgBullish} bars ${r.avgBearish} bars ${r.currentRun} (${r.currentDir}) ${r.pctOfAvg}% ${r.risk} `; }); } table.innerHTML = html; } function updatePriceChart() { const data = currentData.slice(-60); const ctx = document.getElementById('priceChart'); if (!ctx) return; if (priceChart) priceChart.destroy(); const fib = window.fibLevels || {}; const colors = data.map(d => parseFloat(d.Close) >= parseFloat(d.Open) ? '#10b981' : '#ef4444'); priceChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(d => (d.Date || '').split('T')[0].slice(5)), datasets: [{ label: 'Price', data: data.map(d => parseFloat(d.Close) || 0), backgroundColor: colors, borderColor: colors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, annotation: { annotations: { r1: { type: 'line', yMin: fib.fib100, yMax: fib.fib100, borderColor: '#ef4444', borderWidth: 1, borderDash: [5,5] }, s1: { type: 'line', yMin: fib.fib382, yMax: fib.fib382, borderColor: '#10b981', borderWidth: 1, borderDash: [5,5] }, s2: { type: 'line', yMin: fib.fib500, yMax: fib.fib500, borderColor: '#14b8a6', borderWidth: 1, borderDash: [5,5] } }} }, scales: { y: { ticks: { callback: v => '$' + v } } } } }); } // ============================================ // AI ASSISTANT - IMPROVED // ============================================ function askAi(question) { document.getElementById('aiInput').value = question; sendAiMessage(); } async function sendAiMessage() { const input = document.getElementById('aiInput'); const message = input.value.trim(); if (!message) return; const messagesDiv = document.getElementById('aiChatMessages'); messagesDiv.innerHTML += `
    ${escapeHtml(message)}
    `; input.value = ''; messagesDiv.innerHTML += `
    `; messagesDiv.scrollTop = messagesDiv.scrollHeight; document.getElementById('aiSendBtn').disabled = true; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); try { const response = await generateAiResponse(message); document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    ${response}
    `; conversationHistory.push({ role: 'assistant', content: response }); } catch (e) { document.getElementById('typingIndicator')?.remove(); messagesDiv.innerHTML += `
    Sorry, I encountered an error. Please try again.
    `; } document.getElementById('aiSendBtn').disabled = false; messagesDiv.scrollTop = messagesDiv.scrollHeight; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function generateAiResponse(userMessage) { const lowerMsg = userMessage.toLowerCase(); // Build context from dashboard data const dashboardContext = buildDashboardContext(); const systemPrompt = buildSystemPrompt(dashboardContext); // Try Groq Cloud API first if (OLLAMA_CONFIG.enabled) { try { console.log("Calling Groq Cloud API..."); const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(OLLAMA_CONFIG.endpoint + '/chat/completions')}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OLLAMA_CONFIG.apiKey}` }, body: JSON.stringify({ model: OLLAMA_CONFIG.model, messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory.slice(-6), { role: 'user', content: userMessage } ], temperature: 0.7, max_tokens: 1024 }) }); if (response.ok) { const data = await response.json(); const aiResponse = data.choices[0].message.content; // Update conversation history conversationHistory.push({ role: 'user', content: userMessage }); conversationHistory.push({ role: 'assistant', content: aiResponse }); // Keep only last 10 messages if (conversationHistory.length > 10) { conversationHistory = conversationHistory.slice(-10); } console.log("Groq API response received successfully"); return aiResponse; } else { console.warn("Groq API error:", response.status, await response.text()); } } catch (e) { console.warn("Groq API call failed:", e.message); } } // Fallback to local AI responses try { console.log("Using local AI response generator..."); const response = generateLocalResponse(userMessage, dashboardContext); return response; } catch (e) { console.error("AI Response error:", e); return "I encountered an error processing your request. Please try again or check the console for details."; } } function buildSystemPrompt(context) { return `You are the Gann Trading Assistant for Q5D Options Platform. KNOWLEDGE BASE: ${GANN_KNOWLEDGE.tunnelThroughAir} ${GANN_KNOWLEDGE.mystifyingSquare} ${GANN_KNOWLEDGE.divineProportions} ${GANN_KNOWLEDGE.timeCycles} ${GANN_KNOWLEDGE.platformPrinciples} CURRENT DATA: ${context} RULES: 1. Give specific, actionable trading guidance 2. Use actual dashboard data - never hallucinate 3. Reference Gann principles when relevant 4. Format responses clearly with tables when helpful 5. Be concise but thorough`; } function buildDashboardContext() { let context = `CURRENT: ${currentSymbol} | Mode: ${tradingMode.toUpperCase()}\n\n`; if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; context += `${currentSymbol}: $${price.toFixed(2)} | Signal: ${lat.Bias || 'HOLD'} | Consensus: ${lat.PriceConfluence || 0}/4 | ATR: $${atr.toFixed(2)}\n\n`; // Square of 9 levels const sqrtPrice = Math.sqrt(price); context += `Square of 9 for ${currentSymbol}:\n`; context += `• +90° (${(sqrtPrice + 0.25)}²) = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)}\n`; context += `• +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)}\n`; context += `• -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)}\n`; context += `• -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)}\n\n`; } context += `ALL SYMBOLS:\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; context += `${symbol}: $${parseFloat(lat.Close).toFixed(2)} | ${lat.Bias || 'HOLD'} | ${lat.PriceConfluence || 0}/4\n`; } } return context; } function generateLocalResponse(message, context) { const lowerMsg = message.toLowerCase(); // MORNING BREAKOUT PATTERN if (lowerMsg.includes('breakout') || lowerMsg.includes('breaking out') || lowerMsg.includes('breaking above')) { return generateBreakoutResponse(); } // PATTERN SCAN - General if (lowerMsg.includes('pattern') || lowerMsg.includes('setup') || lowerMsg.includes('forming')) { return generatePatternScanResponse(); } // MOMENTUM / TRENDING if (lowerMsg.includes('momentum') || lowerMsg.includes('trending') || lowerMsg.includes('strongest') || lowerMsg.includes('weakest')) { return generateMomentumResponse(); } // REVERSAL PATTERNS if (lowerMsg.includes('reversal') || lowerMsg.includes('turning') || lowerMsg.includes('bottom') || lowerMsg.includes('top')) { return generateReversalResponse(); } // BAR COUNT / CONSECUTIVE BARS if (lowerMsg.includes('bar') && (lowerMsg.includes('count') || lowerMsg.includes('consecutive') || lowerMsg.includes('yesterday') || lowerMsg.includes('closing'))) { return generateBarCountResponse(); } // OPTIMAL TIME TO TRADE if ((lowerMsg.includes('optimal') || lowerMsg.includes('best')) && (lowerMsg.includes('time') || lowerMsg.includes('when'))) { return generateOptimalTimeResponse(); } // WHEN TO TRADE / TRADING HOURS if (lowerMsg.includes('when') && (lowerMsg.includes('trade') || lowerMsg.includes('buy') || lowerMsg.includes('sell'))) { return generateOptimalTimeResponse(); } // HIGH CONSENSUS TRADES if (lowerMsg.includes('consensus') || lowerMsg.includes('3/4') || lowerMsg.includes('4/4') || lowerMsg.includes('high consensus')) { let trades = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; if (consensus >= 3) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; trades.push({ symbol, signal: lat.Bias || 'HOLD', consensus, price: price.toFixed(2), strike: Math.round(price), target: (price + atr * 2).toFixed(2) }); } } } if (trades.length === 0) { return `📊 No ETFs currently have 3/4 or 4/4 consensus. This could mean: • Markets are consolidating • Agents are conflicting • Wait for clearer signals 💡 TIP: Check individual symbols for developing setups, or review the Bar Count tab for trend direction.`; } let response = `📊 **HIGH CONSENSUS TRADES TODAY:**\n\n`; trades.sort((a, b) => b.consensus - a.consensus); trades.forEach(t => { const emoji = t.consensus === 4 ? '🎯' : '✅'; response += `${emoji} **${t.symbol}** - ${t.signal} (${t.consensus}/4)\n`; response += ` Price: $${t.price} | Strike: $${t.strike} | Target: $${t.target}\n\n`; }); const ultra = trades.filter(t => t.consensus === 4); if (ultra.length) { response += `🎯 **TOP PICK:** ${ultra[0].symbol} has ULTRA signal (4/4 consensus)`; } return response; } // BEST CALLS if (lowerMsg.includes('call') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('CALL'); } // BEST PUTS if (lowerMsg.includes('put') && (lowerMsg.includes('best') || lowerMsg.includes('2 day'))) { return generateOptionRecommendations('PUT'); } // SQUARE OF 9 / MYSTIFYING SQUARE if (lowerMsg.includes('square of 9') || lowerMsg.includes('mystifying')) { const price = currentData.length ? parseFloat(currentData[currentData.length - 1].Close) : 675; const sqrtPrice = Math.sqrt(price); return `⬜ **MYSTIFYING SQUARE (SQUARE OF 9) FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Square Root: ${sqrtPrice.toFixed(4)} **RESISTANCE LEVELS (Price Targets):** • +45° = $${Math.pow(sqrtPrice + 0.125, 2).toFixed(2)} (minor) • +90° = $${Math.pow(sqrtPrice + 0.25, 2).toFixed(2)} (important) • +180° = $${Math.pow(sqrtPrice + 0.5, 2).toFixed(2)} (major) • +360° = $${Math.pow(sqrtPrice + 1.0, 2).toFixed(2)} (full cycle) **SUPPORT LEVELS (Stop Areas):** • -45° = $${Math.pow(sqrtPrice - 0.125, 2).toFixed(2)} (minor) • -90° = $${Math.pow(sqrtPrice - 0.25, 2).toFixed(2)} (important) • -180° = $${Math.pow(sqrtPrice - 0.5, 2).toFixed(2)} (major) 💡 **HOW TO USE:** - Buy at support levels - Take profits at resistance - 90° moves are most common - 180° = major reversal zones`; } // DIVINE PROPORTIONS / FIBONACCI if (lowerMsg.includes('divine') || lowerMsg.includes('proportion') || lowerMsg.includes('phi') || lowerMsg.includes('golden')) { const fib = window.fibLevels || {}; return `📐 **DIVINE PROPORTIONS (PHI/FIBONACCI)** The Golden Ratio (Φ = 1.618) governs natural growth patterns and markets follow these proportions. **${currentSymbol} FIBONACCI LEVELS:** 📈 Resistance (Targets): • R1 (100%) = $${fib.fib100?.toFixed(2) || '--'} (Swing High) • R2 (127.2%) = $${fib.fib1272?.toFixed(2) || '--'} (√Φ extension) • R3 (161.8%) = $${fib.fib1618?.toFixed(2) || '--'} (Φ extension) 📉 Support (Stops): • S1 (38.2%) = $${fib.fib382?.toFixed(2) || '--'} (First support) • S2 (50.0%) = $${fib.fib500?.toFixed(2) || '--'} (Key level) • S3 (61.8%) = $${fib.fib618?.toFixed(2) || '--'} (Golden ratio) **TRADING RULES:** • Enter CALL at 61.8% retracement • Target 161.8% extension • Stop below 78.6% level **PHI RELATIONSHIPS:** • 1.618 × 1.618 = 2.618 • 1/1.618 = 0.618 • √1.618 = 1.272`; } // TIME CYCLES if (lowerMsg.includes('time cycle') || lowerMsg.includes('gann cycle') || lowerMsg.includes('when')) { return `⏰ **GANN TIME CYCLES** "When time is up, the trend changes" - W.D. Gann **KEY CYCLE LENGTHS:** • 30 days - Minor cycle • 45 days - Important (1/8 year) • 60 days - Significant • 90 days - Major (quarter) • 120 days - 1/3 year • 144 days - Fibonacci/sacred • 180 days - Half year • 360 days - Full annual cycle **SEASONAL TURN DATES:** • Mar 21 - Spring Equinox • Jun 21 - Summer Solstice • Sep 21 - Fall Equinox • Dec 21 - Winter Solstice **HOW TO APPLY:** 1. Mark the last major high or low date 2. Add Gann numbers (30, 45, 60, 90...) 3. Watch for reversals on those dates 4. Combine with price levels for confirmation`; } // OPTIMAL STRIKE if (lowerMsg.includes('strike') || lowerMsg.includes('optimal')) { if (currentData.length) { const lat = currentData[currentData.length - 1]; const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'CALL'; const sqrtPrice = Math.sqrt(price); return `🎯 **OPTIMAL STRIKE FOR ${currentSymbol}** Current Price: $${price.toFixed(2)} Signal: ${signal} ATR: $${atr.toFixed(2)} **INTRADAY (0DTE):** • ATM Strike: $${Math.round(price)} (Delta ~0.50) • Target: $${(signal === 'PUT' ? price - atr : price + atr).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 0.5 : price - atr * 0.5).toFixed(2)} **SWING (3-5 DTE):** • Strike: $${Math.round(price/5)*5} (slightly OTM) • Target: $${(signal === 'PUT' ? price - atr * 2.5 : price + atr * 2.5).toFixed(2)} • Stop: $${(signal === 'PUT' ? price + atr * 1.5 : price - atr * 1.5).toFixed(2)} **SQUARE OF 9 TARGETS:** • 90° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.25 : 0.25), 2).toFixed(2)} • 180° target: $${Math.pow(sqrtPrice + (signal === 'PUT' ? -0.5 : 0.5), 2).toFixed(2)}`; } return "Please select a symbol to calculate optimal strike."; } // SECTOR COMPARISON if (lowerMsg.includes('sector') || lowerMsg.includes('compare') || lowerMsg.includes('strongest')) { let sectors = []; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length > 20) { const lat = data[data.length - 1]; const prev20 = data[data.length - 21]; const perf = ((parseFloat(lat.Close) - parseFloat(prev20.Close)) / parseFloat(prev20.Close) * 100); sectors.push({ symbol, name: SYMBOL_NAMES[symbol], perf: perf.toFixed(2), signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } sectors.sort((a, b) => parseFloat(b.perf) - parseFloat(a.perf)); let response = `🌍 **SECTOR COMPARISON (20-Day Performance)**\n\n`; sectors.forEach((s, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : (i === 2 ? '🥉' : '📊')); const perfColor = parseFloat(s.perf) >= 0 ? '+' : ''; response += `${emoji} **${s.name}** (${s.symbol})\n`; response += ` ${perfColor}${s.perf}% | ${s.signal} | ${s.consensus}/4 consensus\n\n`; }); response += `\n💡 **RECOMMENDATION:**\n`; response += `• Strongest: ${sectors[0].symbol} - consider for CALL trades\n`; response += `• Weakest: ${sectors[sectors.length-1].symbol} - consider for PUT trades`; return response; } // TUNNEL THROUGH THE AIR if (lowerMsg.includes('tunnel') || lowerMsg.includes('through the air')) { return GANN_KNOWLEDGE.tunnelThroughAir; } // PLATFORM / PRINCIPLES if (lowerMsg.includes('platform') || lowerMsg.includes('principle') || lowerMsg.includes('how does') || lowerMsg.includes('q5d')) { return GANN_KNOWLEDGE.platformPrinciples; } // CURRENT PRICE / STATUS if ((lowerMsg.includes('price') || lowerMsg.includes('status') || lowerMsg.includes('current')) && !lowerMsg.includes('strike')) { return generateCurrentStatusResponse(); } // WHAT SHOULD I TRADE / RECOMMENDATION if (lowerMsg.includes('should i') || lowerMsg.includes('recommend') || lowerMsg.includes('what to trade') || lowerMsg.includes('trade today')) { return generateTradeRecommendation(); } // RISK / STOP LOSS if (lowerMsg.includes('risk') || lowerMsg.includes('stop loss') || lowerMsg.includes('position size')) { return generateRiskManagementResponse(); } // NEWS / MARKET UPDATE if (lowerMsg.includes('news') || lowerMsg.includes('market') || lowerMsg.includes('update')) { return generateMarketUpdateResponse(); } // HELP / WHAT CAN YOU DO if (lowerMsg.includes('help') || lowerMsg.includes('what can you') || lowerMsg.includes('how to use')) { return generateHelpResponse(); } // DEFAULT RESPONSE - More intelligent return generateSmartDefaultResponse(message, context); } function generateOptionRecommendations(direction) { let recommendations = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = (lat.Bias || '').toUpperCase(); if (signal === direction) { const price = parseFloat(lat.Close); const atr = parseFloat(lat.ATR) || 5; recommendations.push({ symbol, price: price.toFixed(2), strike: Math.round(price), target: (direction === 'CALL' ? price + atr * 2 : price - atr * 2).toFixed(2), stop: (direction === 'CALL' ? price - atr : price + atr).toFixed(2), consensus: parseInt(lat.PriceConfluence) || 0, rr: ((atr * 2) / atr).toFixed(1) }); } } } recommendations.sort((a, b) => b.consensus - a.consensus); if (recommendations.length === 0) { return `📊 No strong ${direction} signals found across ETFs. The agents are not aligned for ${direction} trades currently. Consider: • Waiting for better setups • Checking individual symbols • Looking at the opposite direction`; } let response = `📈 **BEST ${direction} OPTIONS (Next 2 Days)**\n\n`; recommendations.slice(0, 5).forEach((r, i) => { const emoji = r.consensus >= 3 ? '🎯' : '📊'; response += `${emoji} **#${i+1} ${r.symbol}** (${r.consensus}/4 consensus)\n`; response += ` Strike: $${r.strike} ${direction}\n`; response += ` Entry: $${r.price} | Target: $${r.target} | Stop: $${r.stop}\n`; response += ` Risk/Reward: 1:${r.rr}\n\n`; }); if (recommendations.length > 0) { const best = recommendations[0]; response += `\n💡 **TOP PICK:** ${best.symbol} $${best.strike} ${direction}\n`; response += `• ${best.consensus}/4 agent consensus\n`; response += `• Target: $${best.target} (${best.rr}:1 R/R)`; } return response; } // NEW: Generate Bar Count Response with actual data function generateBarCountResponse() { let response = `🔢 **BAR COUNT ANALYSIS (as of last close)**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; // Calculate consecutive bars let bullCount = 0, bearCount = 0, currentStreak = 0, lastDir = null; for (let i = Math.max(0, data.length - 50); i < data.length; i++) { const d = data[i]; const isBullish = parseFloat(d.Close) >= parseFloat(d.Open); if (lastDir === null) { lastDir = isBullish; currentStreak = 1; } else if (isBullish === lastDir) { currentStreak++; } else { lastDir = isBullish; currentStreak = 1; } if (isBullish) bullCount++; else bearCount++; } const lat = data[data.length - 1]; const price = parseFloat(lat.Close).toFixed(2); const signal = lat.Bias || 'HOLD'; const streakDir = lastDir ? '📈 Bullish' : '📉 Bearish'; const streakEmoji = currentStreak >= 5 ? '🔥' : (currentStreak >= 3 ? '✅' : ''); response += `**${symbol}** - $${price} | ${signal}\n`; response += ` Current Streak: ${currentStreak} bars ${streakDir} ${streakEmoji}\n`; response += ` Last 50 bars: ${bullCount} bullish / ${bearCount} bearish\n\n`; } // Add interpretation response += `\n📊 **INTERPRETATION:**\n`; response += `• 5+ consecutive bars = Strong trend, consider continuation\n`; response += `• 7+ consecutive bars = Possible exhaustion, watch for reversal\n`; response += `• Mixed (1-2 bars) = Choppy market, wait for direction`; return response; } // NEW: Morning Breakout Pattern Detection function generateBreakoutResponse() { let breakouts = []; let potentialBreakouts = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev5 = data.slice(-6, -1); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevHigh = parseFloat(prev.High); const prevClose = parseFloat(prev.Close); const atr = parseFloat(lat.ATR) || 5; const volume = parseFloat(lat.Volume) || 0; // Calculate average volume const avgVol = data.slice(-20).reduce((sum, d) => sum + (parseFloat(d.Volume) || 0), 0) / 20; const volRatio = volume / avgVol; // Calculate 5-day high const fiveDayHigh = Math.max(...prev5.map(d => parseFloat(d.High) || 0)); // Breakout criteria const aboveOpen = price > open; const nearHigh = (high - price) < (atr * 0.25); // Within 25% of ATR from high const abovePrevHigh = price > prevHigh; const above5DayHigh = price > fiveDayHigh; const highVolume = volRatio > 1.2; const strongMove = ((price - open) / open) * 100 > 0.5; // >0.5% move const breakoutScore = (aboveOpen ? 1 : 0) + (nearHigh ? 1 : 0) + (abovePrevHigh ? 1 : 0) + (above5DayHigh ? 2 : 0) + (highVolume ? 1 : 0) + (strongMove ? 1 : 0); const breakoutData = { symbol, price: price.toFixed(2), change: ((price - prevClose) / prevClose * 100).toFixed(2), volRatio: (volRatio * 100).toFixed(0), score: breakoutScore, above5Day: above5DayHigh, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, target: (price + atr * 1.5).toFixed(2), stop: (price - atr * 0.75).toFixed(2) }; if (breakoutScore >= 5 && above5DayHigh) { breakouts.push(breakoutData); } else if (breakoutScore >= 3) { potentialBreakouts.push(breakoutData); } } let response = `🚀 **MORNING BREAKOUT SCAN**\n\n`; if (breakouts.length > 0) { response += `**🔥 CONFIRMED BREAKOUTS:**\n`; breakouts.sort((a, b) => b.score - a.score); breakouts.forEach(b => { response += `\n**${b.symbol}** - BREAKOUT! ⬆️\n`; response += ` Price: $${b.price} (+${b.change}%)\n`; response += ` Volume: ${b.volRatio}% of avg ${parseFloat(b.volRatio) > 150 ? '🔥' : ''}\n`; response += ` Breaking 5-day high: ${b.above5Day ? '✅ YES' : '❌ No'}\n`; response += ` Agent Signal: ${b.signal} (${b.consensus}/4)\n`; response += ` Target: $${b.target} | Stop: $${b.stop}\n`; }); } else { response += `**No confirmed breakouts at this time.**\n`; } if (potentialBreakouts.length > 0) { response += `\n**👀 POTENTIAL BREAKOUTS (Watch List):**\n`; potentialBreakouts.sort((a, b) => b.score - a.score).slice(0, 4).forEach(b => { response += `• ${b.symbol}: $${b.price} (+${b.change}%) - ${b.signal}\n`; }); } response += `\n**📋 BREAKOUT CRITERIA:**\n`; response += `• Price above open ✓\n`; response += `• Near session high ✓\n`; response += `• Above 5-day high ✓✓\n`; response += `• Volume > 120% avg ✓\n`; response += `• Move > 0.5% ✓\n`; response += `\n**⏰ BEST TIME:** Trade breakouts 9:45-10:30 AM ET`; return response; } // NEW: Pattern Scan Response function generatePatternScanResponse() { let patterns = { breakout: [], pullback: [], reversal: [], consolidation: [] }; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const recent = data.slice(-10); const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const atr = parseFloat(lat.ATR) || 5; const signal = lat.Bias || 'HOLD'; // Get recent highs/lows const recentHighs = recent.map(d => parseFloat(d.High) || 0); const recentLows = recent.map(d => parseFloat(d.Low) || 0); const highestHigh = Math.max(...recentHighs); const lowestLow = Math.min(...recentLows); const range = highestHigh - lowestLow; // Pattern detection const nearHigh = price > highestHigh - (range * 0.1); const nearLow = price < lowestLow + (range * 0.1); const inMiddle = !nearHigh && !nearLow; const tightRange = range < atr * 2; const bullishCandle = price > open; const symbolData = { symbol, price: price.toFixed(2), signal, consensus: lat.PriceConfluence || 0 }; if (nearHigh && bullishCandle && signal === 'CALL') { patterns.breakout.push({...symbolData, type: 'Bullish Breakout'}); } else if (nearLow && !bullishCandle && signal === 'PUT') { patterns.breakout.push({...symbolData, type: 'Bearish Breakdown'}); } else if (inMiddle && signal === 'CALL') { patterns.pullback.push({...symbolData, type: 'Bullish Pullback'}); } else if (inMiddle && signal === 'PUT') { patterns.pullback.push({...symbolData, type: 'Bearish Rally'}); } else if (tightRange) { patterns.consolidation.push({...symbolData, type: 'Tight Range'}); } } let response = `📊 **PATTERN SCAN RESULTS**\n\n`; if (patterns.breakout.length) { response += `**🚀 BREAKOUT PATTERNS:**\n`; patterns.breakout.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.pullback.length) { response += `**📉 PULLBACK/RETRACEMENT:**\n`; patterns.pullback.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price} (${p.consensus}/4)\n`; }); response += `\n`; } if (patterns.consolidation.length) { response += `**📦 CONSOLIDATION (Watch for breakout):**\n`; patterns.consolidation.forEach(p => { response += `• ${p.symbol}: ${p.type} @ $${p.price}\n`; }); response += `\n`; } if (patterns.breakout.length === 0 && patterns.pullback.length === 0) { response += `No clear patterns detected. Market may be choppy.\n\n`; } response += `💡 **TIP:** Breakouts need volume confirmation. Pullbacks offer better risk/reward.`; return response; } // NEW: Momentum Response function generateMomentumResponse() { let momentum = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev5 = data[data.length - 6]; const prev20 = data[data.length - 21]; const price = parseFloat(lat.Close); const price5 = parseFloat(prev5?.Close) || price; const price20 = parseFloat(prev20?.Close) || price; const chg5 = ((price - price5) / price5 * 100); const chg20 = ((price - price20) / price20 * 100); // Count consecutive bars let streak = 0; for (let i = data.length - 1; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; const bullish = parseFloat(d.Close) >= parseFloat(d.Open); if (i === data.length - 1) { streak = bullish ? 1 : -1; } else if ((bullish && streak > 0) || (!bullish && streak < 0)) { streak += bullish ? 1 : -1; } else { break; } } momentum.push({ symbol, price: price.toFixed(2), chg5: chg5.toFixed(2), chg20: chg20.toFixed(2), streak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0, score: chg5 + (streak * 0.5) }); } // Sort by momentum score momentum.sort((a, b) => b.score - a.score); let response = `📈 **MOMENTUM RANKINGS**\n\n`; response += `**🔥 STRONGEST (Bullish Momentum):**\n`; momentum.slice(0, 3).forEach((m, i) => { const emoji = i === 0 ? '🥇' : (i === 1 ? '🥈' : '🥉'); response += `${emoji} **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | 20-Day: ${parseFloat(m.chg20) >= 0 ? '+' : ''}${m.chg20}%\n`; response += ` Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars | Signal: ${m.signal} (${m.consensus}/4)\n\n`; }); response += `**📉 WEAKEST (Bearish Momentum):**\n`; momentum.slice(-3).reverse().forEach((m, i) => { response += `${i + 1}. **${m.symbol}** - $${m.price}\n`; response += ` 5-Day: ${parseFloat(m.chg5) >= 0 ? '+' : ''}${m.chg5}% | Streak: ${m.streak > 0 ? '+' : ''}${m.streak} bars\n\n`; }); response += `💡 **TRADING TIP:**\n`; response += `• Strong momentum + 3/4 consensus = High probability CALL\n`; response += `• Weak momentum + PUT signal = Consider PUT trades`; return response; } // NEW: Reversal Pattern Response function generateReversalResponse() { let reversals = []; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (!data || data.length < 20) continue; const lat = data[data.length - 1]; const prev = data[data.length - 2]; const prev2 = data[data.length - 3]; const price = parseFloat(lat.Close); const open = parseFloat(lat.Open); const high = parseFloat(lat.High); const low = parseFloat(lat.Low); const prevClose = parseFloat(prev.Close); const prevOpen = parseFloat(prev.Open); const atr = parseFloat(lat.ATR) || 5; // Calculate bar counts let bearStreak = 0, bullStreak = 0; for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) < parseFloat(d.Open)) bearStreak++; else break; } for (let i = data.length - 2; i >= Math.max(0, data.length - 10); i--) { const d = data[i]; if (parseFloat(d.Close) >= parseFloat(d.Open)) bullStreak++; else break; } // Reversal patterns const bullishEngulfing = prevClose < prevOpen && price > open && price > prevOpen && open < prevClose; const bearishEngulfing = prevClose > prevOpen && price < open && price < prevOpen && open > prevClose; const hammer = (open - low) > (high - open) * 2 && price > open && bearStreak >= 3; const shootingStar = (high - open) > (open - low) * 2 && price < open && bullStreak >= 3; let pattern = null; let direction = null; if (bullishEngulfing || hammer) { pattern = bullishEngulfing ? 'Bullish Engulfing' : 'Hammer'; direction = 'BULLISH'; } else if (bearishEngulfing || shootingStar) { pattern = bearishEngulfing ? 'Bearish Engulfing' : 'Shooting Star'; direction = 'BEARISH'; } else if (bearStreak >= 5 && price > open) { pattern = 'Potential Bullish Reversal'; direction = 'BULLISH'; } else if (bullStreak >= 5 && price < open) { pattern = 'Potential Bearish Reversal'; direction = 'BEARISH'; } if (pattern) { reversals.push({ symbol, pattern, direction, price: price.toFixed(2), priorStreak: direction === 'BULLISH' ? bearStreak : bullStreak, signal: lat.Bias || 'HOLD', consensus: lat.PriceConfluence || 0 }); } } let response = `🔄 **REVERSAL PATTERN SCAN**\n\n`; if (reversals.length === 0) { response += `No clear reversal patterns detected.\n\n`; response += `**What to look for:**\n`; response += `• 5+ bar downtrend + bullish candle = Potential bottom\n`; response += `• 5+ bar uptrend + bearish candle = Potential top\n`; response += `• Engulfing patterns at key Fibonacci levels\n`; } else { const bullish = reversals.filter(r => r.direction === 'BULLISH'); const bearish = reversals.filter(r => r.direction === 'BEARISH'); if (bullish.length) { response += `**📈 BULLISH REVERSALS (Potential Bottoms):**\n`; bullish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior downtrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } if (bearish.length) { response += `**📉 BEARISH REVERSALS (Potential Tops):**\n`; bearish.forEach(r => { response += `• **${r.symbol}**: ${r.pattern} @ $${r.price}\n`; response += ` Prior uptrend: ${r.priorStreak} bars | Signal: ${r.signal}\n\n`; }); } } response += `⚠️ **CAUTION:** Reversals need confirmation. Wait for follow-through!`; return response; } // NEW: Generate Optimal Trading Time Response function generateOptimalTimeResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close).toFixed(2) : '--'; const signal = lat ? (lat.Bias || 'HOLD') : '--'; return `⏰ **OPTIMAL TIMES TO TRADE ${currentSymbol} OPTIONS** **INTRADAY (0DTE) BEST WINDOWS:** 🌅 **9:30-10:00 AM ET - Opening Range** • Highest volatility & liquidity • Wait for first 5-15 min to establish range • Best for momentum plays • ⚠️ Avoid trading first 5 minutes (too erratic) 📊 **10:00-11:30 AM ET - Morning Trend** • Trend typically established • Best time for directional trades • Options premiums still reasonable • ✅ RECOMMENDED for 0DTE entries ☀️ **11:30 AM-2:00 PM ET - Midday Lull** • Lower volatility, choppy action • ⚠️ Avoid new positions • Good for managing existing trades 🚀 **2:00-3:00 PM ET - Power Hour Setup** • Institutional positioning begins • Good for swing entries (1-5 DTE) • Watch for trend continuation/reversal 🔥 **3:00-4:00 PM ET - Power Hour** • High volume, strong moves • Last chance for 0DTE • ⚠️ Wide spreads near close • Best for experienced traders **SWING TRADES (3-5 DTE) BEST TIMES:** • Enter: 10:00-11:00 AM (after opening noise) • Avoid: Fridays (weekend theta decay) • Best days: Tuesday-Thursday **CURRENT ${currentSymbol} STATUS:** • Price: $${price} • Signal: ${signal} • Mode: ${tradingMode.toUpperCase()} **GANN TIME FACTORS:** • Strongest moves: First hour, last hour • Weakest: 12:00-1:00 PM (lunch hour) • Watch 90-minute cycles intraday (Gann) **🎯 MY RECOMMENDATION:** For ${currentSymbol} ${signal} signal: ${signal === 'CALL' ? '• Enter on pullback to support 10:00-11:00 AM' : '• Enter on rally to resistance 10:00-11:00 AM'} • Set stop based on ATR ($${lat ? parseFloat(lat.ATR).toFixed(2) : '--'}) • Take profits before 3:30 PM for 0DTE`; } // Current Status Response function generateCurrentStatusResponse() { let response = `📊 **CURRENT MARKET STATUS**\n\n`; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const prev = data[data.length - 2] || lat; const price = parseFloat(lat.Close); const prevPrice = parseFloat(prev.Close); const chg = price - prevPrice; const pct = ((chg / prevPrice) * 100).toFixed(2); const signal = lat.Bias || 'HOLD'; const consensus = lat.PriceConfluence || 0; const isCurrentSym = symbol === currentSymbol; response += `${isCurrentSym ? '➤ ' : ''}**${symbol}**: $${price.toFixed(2)} (${chg >= 0 ? '+' : ''}${pct}%)\n`; response += ` Signal: ${signal} | Consensus: ${consensus}/4\n\n`; } } return response; } // Trade Recommendation function generateTradeRecommendation() { let bestTrade = null; let bestScore = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const consensus = parseInt(lat.PriceConfluence) || 0; const signal = lat.Bias || 'HOLD'; if (consensus > bestScore && signal !== 'HOLD') { bestScore = consensus; bestTrade = { symbol, signal, consensus, price: parseFloat(lat.Close), atr: parseFloat(lat.ATR) || 5 }; } } } if (!bestTrade || bestScore < 2) { return `🤔 **NO STRONG TRADE RECOMMENDATIONS RIGHT NOW** Current market conditions don't show clear setups: • No symbols have 3/4 or 4/4 consensus • Agents are conflicting • Consider staying flat or reducing size **WHAT TO DO:** • Wait for clearer signals • Review the Bar Count tab for trend direction • Check back after market open for fresh data`; } const strike = Math.round(bestTrade.price); const target = bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr * 2 : bestTrade.price - bestTrade.atr * 2; const stop = bestTrade.signal === 'CALL' ? bestTrade.price - bestTrade.atr : bestTrade.price + bestTrade.atr; return `🎯 **TODAY'S TOP TRADE RECOMMENDATION** **${bestTrade.symbol} ${bestTrade.signal}** Agent Consensus: ${bestTrade.consensus}/4 ${bestTrade.consensus >= 3 ? '✅' : '⚠️'} 📍 **ENTRY:** • Price: $${bestTrade.price.toFixed(2)} • Strike: $${strike} ${bestTrade.signal} • Mode: ${tradingMode === 'intraday' ? '0DTE' : '3-5 DTE'} 🎯 **TARGETS:** • Target 1: $${(bestTrade.signal === 'CALL' ? bestTrade.price + bestTrade.atr : bestTrade.price - bestTrade.atr).toFixed(2)} • Target 2: $${target.toFixed(2)} 🛑 **RISK:** • Stop Loss: $${stop.toFixed(2)} • Risk/Reward: 1:2 ⏰ **TIMING:** • Best entry: 10:00-11:00 AM ET • Avoid: First 15 min, last 30 min (0DTE)`; } // Risk Management Response function generateRiskManagementResponse() { const lat = currentData.length ? currentData[currentData.length - 1] : null; const price = lat ? parseFloat(lat.Close) : 675; const atr = lat ? parseFloat(lat.ATR) || 5 : 5; return `🛡️ **RISK MANAGEMENT GUIDELINES** **POSITION SIZING (Kelly Criterion):** • Risk 0.5-1% per trade (intraday) • Risk 1-2% per trade (swing) • Never risk more than 5% daily **EXAMPLE for $10,000 Account:** • 1% risk = $100 max loss per trade • If stop is $${atr.toFixed(2)} away (1 ATR) • Max position = $100 ÷ $${atr.toFixed(2)} = ${(100/atr).toFixed(0)} contracts **STOP LOSS PLACEMENT:** • Intraday: 0.5-1.0 ATR = $${(atr * 0.75).toFixed(2)} • Swing: 1.5-2.0 ATR = $${(atr * 1.5).toFixed(2)} **${currentSymbol} CURRENT LEVELS:** • Price: $${price.toFixed(2)} • ATR: $${atr.toFixed(2)} • Intraday Stop: $${(price - atr * 0.75).toFixed(2)} (CALL) / $${(price + atr * 0.75).toFixed(2)} (PUT) • Swing Stop: $${(price - atr * 1.5).toFixed(2)} (CALL) / $${(price + atr * 1.5).toFixed(2)} (PUT) **GANN'S RISK RULES:** • Never let a profit turn into a loss • Move stop to breakeven after 1 ATR profit • Scale out: 50% at T1, 50% at T2 • Max 3 losses in a row = stop trading for day`; } // Market Update Response function generateMarketUpdateResponse() { let bullish = 0, bearish = 0, neutral = 0; for (const symbol of SYMBOLS) { const data = symbol === currentSymbol ? currentData : allSymbolsData[symbol]; if (data && data.length) { const signal = data[data.length - 1].Bias || 'HOLD'; if (signal === 'CALL') bullish++; else if (signal === 'PUT') bearish++; else neutral++; } } const marketBias = bullish > bearish ? 'BULLISH' : (bearish > bullish ? 'BEARISH' : 'MIXED'); const biasEmoji = marketBias === 'BULLISH' ? '📈' : (marketBias === 'BEARISH' ? '📉' : '↔️'); return `📰 **MARKET UPDATE** ${biasEmoji} **OVERALL BIAS: ${marketBias}** • Bullish signals: ${bullish}/${SYMBOLS.length} ETFs • Bearish signals: ${bearish}/${SYMBOLS.length} ETFs • Neutral: ${neutral}/${SYMBOLS.length} ETFs **SECTOR BREAKDOWN:** ${generateQuickSectorSummary()} **TRADING IMPLICATION:** ${marketBias === 'BULLISH' ? '• Favor CALL trades\n• Look for pullbacks to support\n• Sector leaders: Check XLK, XLF' : marketBias === 'BEARISH' ? '• Favor PUT trades\n• Look for rallies to resistance\n• Defensive sectors may outperform' : '• Mixed signals - reduce position size\n• Wait for clearer direction\n• Focus on highest consensus trades'} 💡 Use "high consensus trades" to find specific setups.`; } function generateQuickSectorSummary() { let summary = ''; for (const symbol of ['XLK', 'XLF', 'XLV', 'XLE']) { const data = allSymbolsData[symbol]; if (data && data.length) { const lat = data[data.length - 1]; const signal = lat.Bias || 'HOLD'; const emoji = signal === 'CALL' ? '📈' : (signal === 'PUT' ? '📉' : '➡️'); summary += `${emoji} ${SYMBOL_NAMES[symbol]}: ${signal}\n`; } } return summary; } // Help Response function generateHelpResponse() { return `🆘 **HOW TO USE THE AI ASSISTANT** **QUICK ACTIONS (Click the buttons!):** • 📊 High Consensus - Find best trades • 📈 Best CALLs - Top bullish plays • 📉 Best PUTs - Top bearish plays • 🎯 Optimal Strike - Get strike recommendations **EXAMPLE QUESTIONS:** 📊 **Data & Analysis:** • "Show me bar count for all ETFs" • "What's the current status?" • "Compare all sectors" 🎯 **Trade Ideas:** • "What should I trade today?" • "Best CALL options for 2 days" • "Optimal strike for SPY" • "Show high consensus trades" ⏰ **Timing:** • "When is the best time to trade?" • "What are Gann time cycles?" 📚 **Education:** • "Explain Square of 9" • "What is Divine Proportions?" • "Tunnel Through the Air principles" 🛡️ **Risk:** • "How should I size my position?" • "Where to place stop loss?" Just type naturally - I understand most trading questions!`; } // Smart Default Response function generateSmartDefaultResponse(message, context) { // Try to extract intent const lowerMsg = message.toLowerCase(); // Check for symbol mentions const mentionedSymbol = SYMBOLS.find(s => lowerMsg.includes(s.toLowerCase())); let response = `🤔 I want to help! Let me clarify...\n\n`; response += `You asked: "${message}"\n\n`; if (mentionedSymbol) { const data = allSymbolsData[mentionedSymbol] || currentData; if (data && data.length) { const lat = data[data.length - 1]; response += `**${mentionedSymbol} Quick Info:**\n`; response += `• Price: $${parseFloat(lat.Close).toFixed(2)}\n`; response += `• Signal: ${lat.Bias || 'HOLD'}\n`; response += `• Consensus: ${lat.PriceConfluence || 0}/4\n\n`; } } response += `**Try asking:**\n`; response += `• "What should I trade today?"\n`; response += `• "Show me ${currentSymbol} bar count"\n`; response += `• "Best time to trade options"\n`; response += `• "Calculate Square of 9 for ${currentSymbol}"\n`; response += `• "High consensus trades"\n\n`; response += `Or click a Quick Action button on the right →`; return response; } // ============================================ // TRADE LOG SYSTEM // ============================================ // Initialize trades from localStorage let trades = []; function loadTrades() { const stored = localStorage.getItem('sa4_trades'); if (stored) { try { trades = JSON.parse(stored); } catch (e) { trades = []; } } renderTrades(); updateTradeStats(); // Set default date to today const today = new Date().toISOString().split('T')[0]; const dateInput = document.getElementById('tradeDate'); if (dateInput) dateInput.value = today; } function saveTrades() { localStorage.setItem('sa4_trades', JSON.stringify(trades)); } function logTrade(event) { event.preventDefault(); const trade = { id: Date.now(), date: document.getElementById('tradeDate').value, symbol: document.getElementById('tradeSymbol').value, direction: document.getElementById('tradeDirection').value, type: document.getElementById('tradeType').value, strike: parseFloat(document.getElementById('tradeStrike').value), contracts: parseInt(document.getElementById('tradeContracts').value), entry: parseFloat(document.getElementById('tradeEntry').value), exit: parseFloat(document.getElementById('tradeExit').value) || null, consensus: document.getElementById('tradeConsensus').value, status: document.getElementById('tradeStatus').value, notes: document.getElementById('tradeNotes').value, createdAt: new Date().toISOString() }; // Calculate P&L if closed if (trade.exit && trade.status !== 'OPEN') { trade.pnl = (trade.exit - trade.entry) * trade.contracts * 100; } else { trade.pnl = null; } trades.unshift(trade); // Add to beginning saveTrades(); renderTrades(); updateTradeStats(); clearTradeForm(); // Show success message alert('✅ Trade logged successfully!'); } function clearTradeForm() { document.getElementById('tradeForm').reset(); const today = new Date().toISOString().split('T')[0]; document.getElementById('tradeDate').value = today; document.getElementById('tradeSymbol').value = currentSymbol; } function toggleExitField() { const status = document.getElementById('tradeStatus').value; const exitField = document.getElementById('tradeExit'); if (status === 'OPEN') { exitField.value = ''; exitField.disabled = true; } else { exitField.disabled = false; } } function renderTrades() { const tbody = document.getElementById('tradeTableBody'); if (!tbody) return; const filterSymbol = document.getElementById('filterSymbol')?.value || 'ALL'; const filterDirection = document.getElementById('filterDirection')?.value || 'ALL'; const filterStatus = document.getElementById('filterStatus')?.value || 'ALL'; let filteredTrades = trades.filter(t => { if (filterSymbol !== 'ALL' && t.symbol !== filterSymbol) return false; if (filterDirection !== 'ALL' && t.direction !== filterDirection) return false; if (filterStatus !== 'ALL' && t.status !== filterStatus) return false; return true; }); // Sort by date descending (newest first) filteredTrades.sort((a, b) => new Date(b.date) - new Date(a.date)); var el=document.getElementById('tradeCount');if(el)el.textContent = `(${filteredTrades.length} trades)`; if (filteredTrades.length === 0) { tbody.innerHTML = 'No trades match your filters.'; return; } let html = ''; filteredTrades.forEach((trade, index) => { const pnlClass = trade.pnl > 0 ? 'pnl-positive' : (trade.pnl < 0 ? 'pnl-negative' : ''); const pnlDisplay = trade.pnl !== null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl.toFixed(2)}` : '--'; const statusClass = trade.status.toLowerCase(); const directionEmoji = trade.direction === 'CALL' ? '📈' : '📉'; const typeEmoji = trade.type === 'INTRADAY' ? '⚡' : '🌊'; html += ` ${index + 1} ${trade.date} ${trade.symbol} ${directionEmoji} ${trade.direction} ${typeEmoji} $${trade.strike} ${trade.contracts} $${trade.entry.toFixed(2)} ${trade.exit ? '$' + trade.exit.toFixed(2) : '--'} ${pnlDisplay} ${trade.consensus} ${trade.status} ${trade.notes || '--'} `; }); tbody.innerHTML = html; } function filterTrades() { renderTrades(); } function editTrade(id) { const trade = trades.find(t => t.id === id); if (!trade) return; document.getElementById('tradeDate').value = trade.date; document.getElementById('tradeSymbol').value = trade.symbol; document.getElementById('tradeDirection').value = trade.direction; document.getElementById('tradeType').value = trade.type; document.getElementById('tradeStrike').value = trade.strike; document.getElementById('tradeContracts').value = trade.contracts; document.getElementById('tradeEntry').value = trade.entry; document.getElementById('tradeExit').value = trade.exit || ''; document.getElementById('tradeConsensus').value = trade.consensus; document.getElementById('tradeStatus').value = trade.status; document.getElementById('tradeNotes').value = trade.notes || ''; // Remove the old trade trades = trades.filter(t => t.id !== id); saveTrades(); // Scroll to form document.getElementById('tradeForm').scrollIntoView({ behavior: 'smooth' }); } function deleteTrade(id) { if (!confirm('Are you sure you want to delete this trade?')) return; trades = trades.filter(t => t.id !== id); saveTrades(); renderTrades(); updateTradeStats(); } function clearAllTrades() { if (!confirm('⚠️ Are you sure you want to delete ALL trades? This cannot be undone!')) return; if (!confirm('This will permanently delete your entire trade history. Continue?')) return; trades = []; saveTrades(); renderTrades(); updateTradeStats(); } function updateTradeStats() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0); const losses = closedTrades.filter(t => t.pnl <= 0); // Basic stats var el=document.getElementById('statTotalTrades');if(el)el.textContent = trades.length; // Win rate const winRate = closedTrades.length > 0 ? (wins.length / closedTrades.length * 100).toFixed(1) : 0; var el=document.getElementById('statWinRate');if(el)el.textContent = winRate + '%'; el=document.getElementById('statWinRate');if(el)el.className = `metric-value ${parseFloat(winRate) >= 50 ? 'positive' : 'negative'}`; var el=document.getElementById('statWinLoss');if(el)el.textContent = `${wins.length}W / ${losses.length}L`; // Total P&L const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); var el=document.getElementById('statTotalPnL');if(el)el.textContent = `${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)}`; el=document.getElementById('statTotalPnL');if(el)el.className = `metric-value ${totalPnL >= 0 ? 'positive' : 'negative'}`; // Average trade const avgTrade = closedTrades.length > 0 ? totalPnL / closedTrades.length : 0; var el=document.getElementById('statAvgTrade');if(el)el.textContent = `Avg: ${avgTrade >= 0 ? '+' : ''}$${avgTrade.toFixed(2)}`; // Best/Worst trade if (closedTrades.length > 0) { const best = closedTrades.reduce((max, t) => t.pnl > max.pnl ? t : max); const worst = closedTrades.reduce((min, t) => t.pnl < min.pnl ? t : min); var el=document.getElementById('statBestTrade');if(el)el.textContent = `+$${best.pnl.toFixed(2)}`; var el=document.getElementById('statBestSymbol');if(el)el.textContent = `${best.symbol} ${best.direction}`; var el=document.getElementById('statWorstTrade');if(el)el.textContent = `$${worst.pnl.toFixed(2)}`; var el=document.getElementById('statWorstSymbol');if(el)el.textContent = `${worst.symbol} ${worst.direction}`; } // Trades this week const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const weekTrades = trades.filter(t => new Date(t.date) >= oneWeekAgo); var el=document.getElementById('statTradesThisWeek');if(el)el.textContent = `${weekTrades.length} this week`; // Update analytics updateSymbolPerformance(); updateDirectionPerformance(); updateConsensusAnalysis(); } function updateSymbolPerformance() { const container = document.getElementById('symbolPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see performance breakdown

    '; return; } const bySymbol = {}; closedTrades.forEach(t => { if (!bySymbol[t.symbol]) { bySymbol[t.symbol] = { wins: 0, losses: 0, pnl: 0 }; } bySymbol[t.symbol].pnl += t.pnl; if (t.pnl > 0) bySymbol[t.symbol].wins++; else bySymbol[t.symbol].losses++; }); let html = '
    '; Object.entries(bySymbol).sort((a, b) => b[1].pnl - a[1].pnl).forEach(([symbol, data]) => { const winRate = ((data.wins / (data.wins + data.losses)) * 100).toFixed(0); const pnlClass = data.pnl >= 0 ? 'positive' : 'negative'; html += `
    ${symbol} ${data.wins}W/${data.losses}L (${winRate}%) ${data.pnl >= 0 ? '+' : ''}$${data.pnl.toFixed(2)}
    `; }); html += '
    '; container.innerHTML = html; } function updateDirectionPerformance() { const container = document.getElementById('directionPerformance'); if (!container) return; const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); if (closedTrades.length === 0) { container.innerHTML = '

    Log closed trades to see CALL vs PUT performance

    '; return; } const calls = closedTrades.filter(t => t.direction === 'CALL'); const puts = closedTrades.filter(t => t.direction === 'PUT'); const callWins = calls.filter(t => t.pnl > 0).length; const callPnL = calls.reduce((sum, t) => sum + t.pnl, 0); const putWins = puts.filter(t => t.pnl > 0).length; const putPnL = puts.reduce((sum, t) => sum + t.pnl, 0); const callWinRate = calls.length > 0 ? ((callWins / calls.length) * 100).toFixed(0) : 0; const putWinRate = puts.length > 0 ? ((putWins / puts.length) * 100).toFixed(0) : 0; container.innerHTML = `
    📈 CALLS
    ${calls.length} trades | ${callWinRate}% win rate
    ${callPnL >= 0 ? '+' : ''}$${callPnL.toFixed(2)}
    📉 PUTS
    ${puts.length} trades | ${putWinRate}% win rate
    ${putPnL >= 0 ? '+' : ''}$${putPnL.toFixed(2)}
    `; } function updateConsensusAnalysis() { const consensusLevels = ['4/4', '3/4', '2/4', '1/4']; const ids = ['ultra44', 'super34', 'single24', 'weak14']; consensusLevels.forEach((level, i) => { const levelTrades = trades.filter(t => t.consensus === level && t.status !== 'OPEN' && t.pnl !== null); const wins = levelTrades.filter(t => t.pnl > 0).length; const winRate = levelTrades.length > 0 ? ((wins / levelTrades.length) * 100).toFixed(0) : '--'; document.getElementById(`${ids[i]}WinRate`).textContent = winRate + '%'; document.getElementById(`${ids[i]}WinRate`).style.color = winRate !== '--' && parseFloat(winRate) >= 50 ? 'var(--success)' : 'var(--danger)'; document.getElementById(`${ids[i]}Count`).textContent = `${levelTrades.length} trades`; }); } // Export Functions function printTrades() { window.print(); } function exportCSV() { if (trades.length === 0) { alert('No trades to export!'); return; } const headers = ['Date', 'Symbol', 'Direction', 'Type', 'Strike', 'Contracts', 'Entry', 'Exit', 'P&L', 'Consensus', 'Status', 'Notes']; const rows = trades.map(t => [ t.date, t.symbol, t.direction, t.type, t.strike, t.contracts, t.entry, t.exit || '', t.pnl || '', t.consensus, t.status, `"${(t.notes || '').replace(/"/g, '""')}"` ]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `trade_log_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); } function shareTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const text = `📊 Stock Agent 4 - Trade Summary 📈 Total Trades: ${trades.length} 🎯 Win Rate: ${winRate}% 💰 Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} 📅 Period: ${trades.length > 0 ? trades[trades.length-1].date : 'N/A'} to ${trades.length > 0 ? trades[0].date : 'N/A'} Powered by Q5D Options Platform & Gann Principles 🔮`; if (navigator.share) { navigator.share({ title: 'Stock Agent 4 Trade Summary', text: text }).catch(console.log); } else { // Fallback - copy to clipboard navigator.clipboard.writeText(text).then(() => { alert('📋 Trade summary copied to clipboard!'); }).catch(() => { prompt('Copy this trade summary:', text); }); } } function emailTrades() { const closedTrades = trades.filter(t => t.status !== 'OPEN' && t.pnl !== null); const wins = closedTrades.filter(t => t.pnl > 0).length; const winRate = closedTrades.length > 0 ? ((wins / closedTrades.length) * 100).toFixed(1) : 0; const totalPnL = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0); const subject = encodeURIComponent('Stock Agent 4 - Trade Summary'); const body = encodeURIComponent(`Stock Agent 4 - Trade Summary Total Trades: ${trades.length} Win Rate: ${winRate}% Total P&L: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} Recent Trades: ${trades.slice(0, 10).map(t => `- ${t.date}: ${t.symbol} ${t.direction} ${t.contracts}x $${t.strike} | P&L: ${t.pnl !== null ? '$' + t.pnl.toFixed(2) : 'Open'}`).join('\n')} --- Powered by Q5D Options Platform`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Initialize trade log when app loads async function initializeApp() { initTabs(); await loadAllData(); // Wait for data to load // Update Options Calculator with new symbol updateOptionsCalcForSymbol(); await loadAllSymbolsData(); loadTrades(); // Load trade history runHealthCheck(); // Now run health check AFTER data is loaded renderSystemLog(); // Render existing log entries addSystemLogEntry('Data Updated (' + currentSymbol + ')', 'Success'); // Log data load setInterval(loadAllData, 60000); setInterval(runHealthCheck, 300000); // Health check every 5 minutes } // ============================================ // HEALTH MONITORING SYSTEM // ============================================ let healthStatus = { dataFreshness: 'unknown', marketStatus: 'unknown', workflowStatus: 'unknown', dataQuality: 'unknown', alerts: [] }; function runHealthCheck() { console.log('Running health check...'); healthStatus.alerts = []; // Check data freshness checkDataFreshness(); // Check market status checkMarketStatus(); // Check data quality checkDataQuality(); // Check connections checkConnections(); // Update overall status updateOverallHealth(); // Update last check time var el=document.getElementById('lastHealthCheck');if(el)el.textContent = new Date().toLocaleString(); // Update health timestamp updateSectionTimestamp('health'); } function checkDataFreshness() { console.log('🔍 checkDataFreshness running...'); // Get elements directly const lastDateEl = document.getElementById('lastDataDate'); const daysSinceEl = document.getElementById('daysSinceUpdate'); const expectedEl = document.getElementById('expectedUpdate'); const totalBarsEl = document.getElementById('totalBarsLoaded'); const dateRangeEl = document.getElementById('dataDateRange'); const valueEl = document.getElementById('dataFreshnessValue'); const statusEl = document.getElementById('dataFreshnessStatus'); const freshEl = document.getElementById('healthDataFresh'); // Check if we have data if (!currentData || currentData.length === 0) { console.log('❌ No data available'); if (valueEl) valueEl.textContent = 'No Data'; if (statusEl) statusEl.textContent = 'CRITICAL'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'No data loaded' }); return; } console.log('📊 Data length:', currentData.length); // Get last bar directly const lastBar = currentData[currentData.length - 1]; console.log('📅 Last bar:', lastBar); console.log('📅 Last bar Date:', lastBar.Date); // Parse the date - handle ISO format WITHOUT timezone conversion let lastDate; if (lastBar.Date) { // Extract just the date part (YYYY-MM-DD) to avoid timezone issues const dateStr = lastBar.Date.split('T')[0]; const [year, month, day] = dateStr.split('-').map(Number); lastDate = new Date(year, month - 1, day); // month is 0-indexed console.log('📅 Parsed lastDate (no TZ):', lastDate); } if (!lastDate || isNaN(lastDate.getTime())) { console.log('❌ Invalid date'); if (valueEl) valueEl.textContent = 'Invalid Date'; healthStatus.dataFreshness = 'critical'; return; } // Calculate days difference const now = new Date(); const diffMs = now - lastDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); console.log('📅 Now:', now); console.log('📅 Diff days:', diffDays); // Format the date string const dateStr = lastDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }); console.log('📅 Formatted date:', dateStr); // UPDATE THE ELEMENTS DIRECTLY if (lastDateEl) { lastDateEl.textContent = dateStr; lastDateEl.innerHTML = dateStr; console.log('✅ Updated lastDataDate to:', dateStr); } else { console.log('❌ lastDataDate element not found!'); } if (daysSinceEl) { const daysText = diffDays === 0 ? 'Today' : (diffDays === 1 ? '1 day ago' : diffDays + ' days ago'); daysSinceEl.textContent = daysText; console.log('✅ Updated daysSinceUpdate to:', daysText); } if (totalBarsEl) { totalBarsEl.textContent = currentData.length + ' bars'; } // First date for range const firstBar = currentData[0]; if (firstBar && firstBar.Date && dateRangeEl) { const firstDateStr = firstBar.Date.split('T')[0]; const [fy, fm, fd] = firstDateStr.split('-').map(Number); const firstDate = new Date(fy, fm - 1, fd); dateRangeEl.textContent = firstDate.toLocaleDateString() + ' - ' + lastDate.toLocaleDateString(); } // Expected update if (expectedEl) { const nextUpdate = getNextMarketDay(lastDate); expectedEl.textContent = nextUpdate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } // Status based on days if (diffDays === 0) { if (valueEl) valueEl.textContent = 'Up to Date'; if (statusEl) { statusEl.textContent = 'FRESH'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; healthStatus.alerts.push({ type: 'success', message: 'Data is current' }); } else if (diffDays === 1) { if (valueEl) valueEl.textContent = '1 Day Old'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else if (diffDays <= 3) { const isWeekend = isWeekendGap(lastDate, now); if (isWeekend) { if (valueEl) valueEl.textContent = diffDays + ' Days (Weekend)'; if (statusEl) { statusEl.textContent = 'OK'; statusEl.className = 'health-metric-status ok'; } if (freshEl) freshEl.className = 'health-metric healthy'; healthStatus.dataFreshness = 'healthy'; } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'STALE'; statusEl.className = 'health-metric-status warn'; } if (freshEl) freshEl.className = 'health-metric warning'; healthStatus.dataFreshness = 'warning'; healthStatus.alerts.push({ type: 'warning', message: 'Data is ' + diffDays + ' days old' }); } } else { if (valueEl) valueEl.textContent = diffDays + ' Days Old'; if (statusEl) { statusEl.textContent = 'OUTDATED'; statusEl.className = 'health-metric-status error'; } if (freshEl) freshEl.className = 'health-metric critical'; healthStatus.dataFreshness = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data is ' + diffDays + ' days old!' }); } console.log('✅ checkDataFreshness complete'); } function checkMarketStatus() { const marketEl = document.getElementById('healthMarket'); const valueEl = document.getElementById('marketStatusValue'); const statusEl = document.getElementById('marketStatusStatus'); const now = new Date(); const etOffset = getETOffset(); const etNow = new Date(now.getTime() + etOffset); const day = etNow.getDay(); const hours = etNow.getHours(); const minutes = etNow.getMinutes(); const timeNum = hours * 100 + minutes; // Weekend check if (day === 0 || day === 6) { valueEl.textContent = 'Weekend'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; return; } // Market hours: 9:30 AM - 4:00 PM ET if (timeNum >= 930 && timeNum < 1600) { valueEl.textContent = 'Market Open'; statusEl.textContent = 'TRADING'; statusEl.className = 'health-metric-status ok'; marketEl.className = 'health-metric healthy'; healthStatus.marketStatus = 'open'; } else if (timeNum >= 400 && timeNum < 930) { valueEl.textContent = 'Pre-Market'; statusEl.textContent = 'WAITING'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'premarket'; } else { valueEl.textContent = 'After Hours'; statusEl.textContent = 'CLOSED'; statusEl.className = 'health-metric-status warn'; marketEl.className = 'health-metric warning'; healthStatus.marketStatus = 'closed'; } } function checkDataQuality() { const qualityEl = document.getElementById('healthDataQuality'); const valueEl = document.getElementById('dataQualityValue'); const statusEl = document.getElementById('dataQualityStatus'); if (!currentData || currentData.length === 0) { valueEl.textContent = 'No Data'; statusEl.textContent = 'N/A'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; return; } let issues = 0; let checks = 0; // Check for required fields const lastBar = currentData[currentData.length - 1]; const requiredFields = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']; requiredFields.forEach(field => { checks++; if (!lastBar[field] && lastBar[field] !== 0) { issues++; } }); // Check for valid price data checks++; if (lastBar.Close && (lastBar.Close < 0 || lastBar.Close > 10000)) { issues++; healthStatus.alerts.push({ type: 'warning', message: 'Unusual price detected - verify data integrity' }); } // Check for signal data checks++; if (!lastBar.Bias) { issues++; } // Calculate quality score const score = Math.round(((checks - issues) / checks) * 100); if (score >= 90) { valueEl.textContent = score + '%'; statusEl.textContent = 'EXCELLENT'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 70) { valueEl.textContent = score + '%'; statusEl.textContent = 'GOOD'; statusEl.className = 'health-metric-status ok'; qualityEl.className = 'health-metric healthy'; healthStatus.dataQuality = 'healthy'; } else if (score >= 50) { valueEl.textContent = score + '%'; statusEl.textContent = 'FAIR'; statusEl.className = 'health-metric-status warn'; qualityEl.className = 'health-metric warning'; healthStatus.dataQuality = 'warning'; } else { valueEl.textContent = score + '%'; statusEl.textContent = 'POOR'; statusEl.className = 'health-metric-status error'; qualityEl.className = 'health-metric critical'; healthStatus.dataQuality = 'critical'; healthStatus.alerts.push({ type: 'critical', message: 'Data quality issues detected - check CSV files' }); } } function checkConnections() { // Update symbol display const symbolEl = document.getElementById('connDataSymbol'); if (symbolEl) symbolEl.textContent = currentSymbol; // GitHub Pages - if page loaded, it's working var el=document.getElementById('connGitHubStatus');if(el)el.textContent = 'Connected'; var el=document.getElementById('connGitHubLight');if(el)el.textContent = '🟢'; // Data feed - based on whether data loaded if (currentData && currentData.length > 0) { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Loaded (' + currentData.length + ' bars)'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🟢'; } else { var el=document.getElementById('connDataStatus');if(el)el.textContent = 'Failed'; var el=document.getElementById('connDataLight');if(el)el.textContent = '🔴'; } // Portfolio feed var el=document.getElementById('connPortfolioStatus');if(el)el.textContent = 'Active'; var el=document.getElementById('connPortfolioLight');if(el)el.textContent = '🟢'; // Local Storage try { localStorage.setItem('health_test', 'test'); localStorage.removeItem('health_test'); var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Available'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🟢'; } catch (e) { var el=document.getElementById('connLocalStorageStatus');if(el)el.textContent = 'Blocked'; var el=document.getElementById('connLocalStorageLight');if(el)el.textContent = '🔴'; healthStatus.alerts.push({ type: 'warning', message: 'Local storage unavailable - trade log may not persist' }); } } function updateOverallHealth() { const banner = document.getElementById('healthBanner'); const icon = document.getElementById('healthBannerIcon'); const title = document.getElementById('healthBannerTitle'); const subtitle = document.getElementById('healthBannerSubtitle'); const workflowEl = document.getElementById('healthWorkflow'); const workflowValue = document.getElementById('workflowStatusValue'); const workflowStatus = document.getElementById('workflowStatusStatus'); // Determine workflow status based on data freshness if (healthStatus.dataFreshness === 'healthy') { workflowValue.textContent = 'Running'; workflowStatus.textContent = 'ACTIVE'; workflowStatus.className = 'health-metric-status ok'; workflowEl.className = 'health-metric healthy'; healthStatus.workflowStatus = 'healthy'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟢'; } else if (healthStatus.dataFreshness === 'warning') { workflowValue.textContent = 'Delayed'; workflowStatus.textContent = 'CHECK'; workflowStatus.className = 'health-metric-status warn'; workflowEl.className = 'health-metric warning'; healthStatus.workflowStatus = 'warning'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🟡'; } else { workflowValue.textContent = 'Stopped?'; workflowStatus.textContent = 'ERROR'; workflowStatus.className = 'health-metric-status error'; workflowEl.className = 'health-metric critical'; healthStatus.workflowStatus = 'critical'; var el=document.getElementById('workflowStatusLight');if(el)el.textContent = '🔴'; } // Update traffic lights for data freshness const dataFreshLight = document.getElementById('dataFreshnessLight'); if (dataFreshLight) { if (healthStatus.dataFreshness === 'healthy') dataFreshLight.textContent = '🟢'; else if (healthStatus.dataFreshness === 'warning') dataFreshLight.textContent = '🟡'; else dataFreshLight.textContent = '🔴'; } // Update traffic lights for data quality const dataQualityLight = document.getElementById('dataQualityLight'); if (dataQualityLight) { if (healthStatus.dataQuality === 'healthy') dataQualityLight.textContent = '🟢'; else if (healthStatus.dataQuality === 'warning') dataQualityLight.textContent = '🟡'; else dataQualityLight.textContent = '🔴'; } // Update market status light const marketLight = document.getElementById('marketStatusLight'); if (marketLight) { const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; if (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) { marketLight.textContent = '🟢'; } else { marketLight.textContent = '🟡'; } } // Count traffic lights for summary const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; // Add market status (yellow if closed, green otherwise) const marketVal = document.getElementById('marketStatusValue')?.textContent || ''; const marketStatus = (marketVal.includes('Open') || marketVal.includes('Pre') || marketVal.includes('After')) ? 'healthy' : 'warning'; statuses.push(marketStatus); const greenCount = statuses.filter(s => s === 'healthy').length; const yellowCount = statuses.filter(s => s === 'warning').length; const redCount = statuses.filter(s => s === 'critical').length; var el=document.getElementById('trafficGreenCount');if(el)el.textContent = '🟢 ' + greenCount; var el=document.getElementById('trafficYellowCount');if(el)el.textContent = '🟡 ' + yellowCount; var el=document.getElementById('trafficRedCount');if(el)el.textContent = '🔴 ' + redCount; // Overall status if (redCount > 0) { banner.className = 'health-banner critical'; icon.textContent = '🔴'; title.textContent = 'ATTENTION NEEDED'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow, ${redCount} Red`; } else if (yellowCount > 0) { banner.className = 'health-banner warning'; icon.textContent = '🟡'; title.textContent = 'MINOR ISSUES'; subtitle.textContent = `${greenCount}/4 Green, ${yellowCount} Yellow`; } else { banner.className = 'health-banner'; icon.textContent = '🟢'; title.textContent = 'ALL SYSTEMS OPERATIONAL'; subtitle.textContent = `${greenCount}/4 Green`; } // Update component status table updateComponentStatus(); // Render alerts renderHealthAlerts(); // Update header badge updateHeaderHealthBadge(); // Log the health check event addSystemLogEntry('Health Check Run', 'Success'); } function renderHealthAlerts() { const container = document.getElementById('healthAlerts'); if (healthStatus.alerts.length === 0) { container.innerHTML = `
    No issues detected - system running normally
    `; return; } let html = ''; healthStatus.alerts.forEach(alert => { const iconMap = { critical: '🚨', warning: '⚠️', info: 'ℹ️', success: '✅' }; html += `
    ${iconMap[alert.type] || 'ℹ️'} ${alert.message}
    `; }); container.innerHTML = html; } // System Log Functions function getSystemLog() { try { const log = localStorage.getItem('stockAgentSystemLog'); return log ? JSON.parse(log) : []; } catch (e) { return []; } } function saveSystemLog(log) { try { // Keep only last 20 entries const trimmed = log.slice(-20); localStorage.setItem('stockAgentSystemLog', JSON.stringify(trimmed)); } catch (e) { console.warn('Could not save system log:', e); } } function addSystemLogEntry(event, status) { const log = getSystemLog(); const now = new Date(); log.push({ timestamp: now.toISOString(), dateTime: now.toLocaleString(), event: event, status: status }); saveSystemLog(log); renderSystemLog(); } function clearSystemLog() { try { localStorage.removeItem('stockAgentSystemLog'); renderSystemLog(); } catch (e) { console.warn('Could not clear system log:', e); } } function renderSystemLog() { const tbody = document.getElementById('systemLogTable'); if (!tbody) return; const log = getSystemLog(); if (log.length === 0) { tbody.innerHTML = 'No events logged yet'; return; } // Show most recent first const recentLog = log.slice(-10).reverse(); tbody.innerHTML = recentLog.map(entry => { const statusIcon = entry.status === 'Success' ? '🟢' : entry.status === 'Warning' ? '🟡' : '🔴'; return ` ${entry.dateTime} ${entry.event} ${statusIcon} ${entry.status} `; }).join(''); } // Update Component Status Table function updateComponentStatus() { const now = new Date(); const nowStr = now.toLocaleString(); // Health Check time const healthCheckTimeEl = document.getElementById('compHealthCheckTime'); const healthCheckLightEl = document.getElementById('compHealthCheckLight'); if (healthCheckTimeEl) healthCheckTimeEl.textContent = nowStr; if (healthCheckLightEl) healthCheckLightEl.textContent = '🟢'; // Data Fetch time (based on last data date) const dataFetchTimeEl = document.getElementById('compDataFetchTime'); const dataFetchLightEl = document.getElementById('compDataFetchLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); // Assume data was fetched at 4:05 PM on that date lastDate.setHours(16, 5, 0); if (dataFetchTimeEl) dataFetchTimeEl.textContent = lastDate.toLocaleString(); if (dataFetchLightEl) dataFetchLightEl.textContent = healthStatus.dataFreshness === 'healthy' ? '🟢' : healthStatus.dataFreshness === 'warning' ? '🟡' : '🔴'; } } // Workflow time (same as data fetch) const workflowTimeEl = document.getElementById('compWorkflowTime'); const workflowLightEl = document.getElementById('compWorkflowLight'); if (currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; if (lastBar.Date) { const lastDate = new Date(lastBar.Date); lastDate.setHours(16, 5, 0); if (workflowTimeEl) workflowTimeEl.textContent = lastDate.toLocaleString(); if (workflowLightEl) workflowLightEl.textContent = healthStatus.workflowStatus === 'healthy' ? '🟢' : healthStatus.workflowStatus === 'warning' ? '🟡' : '🔴'; } } // Deploy time (estimate based on health check + 2 min) const deployTimeEl = document.getElementById('compDeployTime'); const deployLightEl = document.getElementById('compDeployLight'); if (deployTimeEl) { const deployTime = new Date(now.getTime() + 2 * 60 * 1000); deployTimeEl.textContent = '~' + deployTime.toLocaleString(); } if (deployLightEl) deployLightEl.textContent = '🟢'; } // Helper functions function getETOffset() { // Rough ET offset (doesn't account for DST perfectly) const now = new Date(); const jan = new Date(now.getFullYear(), 0, 1); const jul = new Date(now.getFullYear(), 6, 1); const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); const isDST = now.getTimezoneOffset() < stdOffset; return isDST ? -4 * 60 * 60 * 1000 : -5 * 60 * 60 * 1000; } function getNextMarketDay(fromDate) { const next = new Date(fromDate); next.setDate(next.getDate() + 1); // Skip weekends while (next.getDay() === 0 || next.getDay() === 6) { next.setDate(next.getDate() + 1); } return next; } function isWeekendGap(lastDate, currentDate) { const lastDay = lastDate.getDay(); const diff = Math.floor((currentDate - lastDate) / (1000 * 60 * 60 * 24)); // If last data was Friday and it's been 2-3 days, it's a weekend gap if (lastDay === 5 && diff <= 3) return true; return false; } function toggleTroubleshoot(header) { const content = header.nextElementSibling; const toggle = header.querySelector('.troubleshoot-toggle'); if (content.classList.contains('show')) { content.classList.remove('show'); toggle.textContent = '+'; } else { content.classList.add('show'); toggle.textContent = '−'; } } function updateHeaderHealthBadge() { const badge = document.getElementById('headerHealthBadge'); const dot = document.getElementById('headerHealthDot'); const text = document.getElementById('headerHealthText'); if (!badge) return; const statuses = [healthStatus.dataFreshness, healthStatus.dataQuality, healthStatus.workflowStatus]; if (statuses.includes('critical')) { badge.className = 'status-badge critical'; badge.style.background = 'rgba(239,68,68,0.3)'; text.textContent = 'ISSUES'; } else if (statuses.includes('warning')) { badge.className = 'status-badge warning'; badge.style.background = 'rgba(245,158,11,0.3)'; text.textContent = 'WARNING'; } else if (statuses.includes('unknown')) { badge.className = 'status-badge'; badge.style.background = 'rgba(255,255,255,0.2)'; text.textContent = 'CHECKING'; } else { badge.className = 'status-badge online'; badge.style.background = 'rgba(16,185,129,0.3)'; text.textContent = 'HEALTHY'; } } function switchToHealthTab() { // Activate health tab document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); const healthTab = document.querySelector('[data-tab="health"]'); const healthContent = document.getElementById('health'); if (healthTab) healthTab.classList.add('active'); if (healthContent) healthContent.classList.add('active'); // Run health check when switching runHealthCheck(); } // ============================================ // ENHANCED MULTI-CHARTS SYSTEM // ============================================ let lwChartInstances = { intraday: null, swing: null, position: null }; let mtfChartData = { intraday: [], swing: [], position: [] }; function toggleMTFSettings(panel) { const el = document.getElementById(`${panel}Settings`); if (el) el.classList.toggle('show'); } function getMTFSettings(panel) { return { showEMA: document.getElementById(`${panel}ShowEMA`)?.checked ?? true, showFib: document.getElementById(`${panel}ShowFib`)?.checked ?? true, showPatterns: document.getElementById(`${panel}ShowPatterns`)?.checked ?? true, showVolume: document.getElementById(`${panel}ShowVolume`)?.checked ?? true }; } // Chart timeframe update function function updateChartTimeframe(chartType, timeframe) { console.log('Updating ' + chartType + ' to ' + timeframe); const timeframeLabels = { '1m': '1 Minute', '5m': '5 Minute', '15m': '15 Minute', '30m': '30 Minute', '1h': '1 Hour', '4h': '4 Hour', '1d': 'Daily', '1w': 'Weekly', '1M': 'Monthly', '3M': 'Quarterly' }; // Update the timeframe indicator const indicatorId = chartType + 'TfIndicator'; const indicator = document.getElementById(indicatorId); if (indicator) { indicator.textContent = 'Showing: ' + (timeframeLabels[timeframe] || timeframe) + ' data'; } // Show notification const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 70px; right: 20px; background: #3b82f6; color: white; padding: 10px 20px; border-radius: 8px; font-size: 12px; z-index: 9999; box-shadow: 0 4px 12px rgba(0,0,0,0.15);'; notification.innerHTML = '' + chartType.charAt(0).toUpperCase() + chartType.slice(1) + ' chart switched to ' + timeframeLabels[timeframe]; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; setTimeout(() => notification.remove(), 300); }, 2000); } function initMultiCharts() { console.log('Initializing enhanced multi-charts...'); updateAllMultiCharts(); } async function syncSymbolFromMultiCharts() { const newSymbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== newSymbol) { mainSelect.value = newSymbol; } await changeSymbol(); } async function updateAllMultiCharts() { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); await updateChart('intraday'); await updateChart('swing'); await updateChart('position'); // Update multi-charts timestamp updateSectionTimestamp('multiCharts'); } function refreshAllCharts() { updateAllMultiCharts(); } // ============================================ // MULTI-CHARTS DATA LOADING & SYMBOL UPDATES // ============================================ // Cache for loaded symbol data const marketData = {}; // Load individual symbol data from CSV async function loadSymbolData(symbol) { try { // Try data/ folder first, then root let response = await fetch(`data/${symbol}.csv`).catch(() => null); if (!response?.ok) { console.log('Could not load data for ' + symbol); return null; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return null; const header = lines[0].split(','); const getIdx = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); const closeIdx = getIdx('close') !== -1 ? getIdx('close') : 4; const openIdx = getIdx('open') !== -1 ? getIdx('open') : 1; const highIdx = getIdx('high') !== -1 ? getIdx('high') : 2; const lowIdx = getIdx('low') !== -1 ? getIdx('low') : 3; const close = parseFloat(cols[closeIdx]) || 0; const open = parseFloat(cols[openIdx]) || 0; const high = parseFloat(cols[highIdx]) || 0; const low = parseFloat(cols[lowIdx]) || 0; const prevClose = parseFloat(prevCols[closeIdx]) || close; // Calculate 20-bar high/low for Fibonacci let high20 = high, low20 = low; for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[highIdx]) || 0; const l = parseFloat(rowCols[lowIdx]) || 0; if (h > high20) high20 = h; if (l > 0 && l < low20) low20 = l; } marketData[symbol] = { price: close, open: open, high: high, low: low, close: close, prevClose: prevClose, change: close - prevClose, changePct: ((close - prevClose) / prevClose * 100), high20: high20, low20: low20, lastUpdate: new Date().toLocaleTimeString() }; console.log('Loaded ' + symbol + ' data: $' + close.toFixed(2)); return marketData[symbol]; } catch (error) { console.log('Error loading ' + symbol + ':', error); return null; } } // Update Multi-Charts with new symbol data async function updateMultiCharts(symbol) { console.log('Updating Multi-Charts for ' + symbol); // Get data for the symbol let symbolData = marketData[symbol]; if (!symbolData) { // Try to load data if not in cache symbolData = await loadSymbolData(symbol); } if (!symbolData) { console.log('No data available for ' + symbol); // Still update the UI with the symbol name } // Update all symbol labels in Multi-Charts document.querySelectorAll('#multi-charts .symbol-label').forEach(el => { el.textContent = symbol; }); // Update panel symbol displays ['intraday', 'swing', 'position'].forEach(panel => { const symbolEl = document.getElementById(`${panel}Symbol`); if (symbolEl) symbolEl.textContent = symbol; }); // Update chart titles document.querySelectorAll('#multi-charts .chart-title, #multi-charts h4').forEach(el => { el.innerHTML = el.innerHTML.replace(/SPY|QQQ|IWM|DIA|XL[A-Z]+/g, symbol); }); if (symbolData) { // Update price displays document.querySelectorAll('#multi-charts .price-display').forEach(el => { el.textContent = '$' + symbolData.price.toFixed(2); }); // Update Fibonacci levels based on new symbol's data updateMultiChartFibLevels(symbol, symbolData); // Update bias indicators updateMultiChartBias(symbol, symbolData); console.log('Multi-Charts updated for ' + symbol + ' at $' + symbolData.price.toFixed(2)); } // Refresh all chart panels with new data await updateAllMultiCharts(); } // Update Fibonacci levels in Multi-Charts function updateMultiChartFibLevels(symbol, data) { if (!data || !data.high20 || !data.low20) return; const range = data.high20 - data.low20; const r1 = data.high20; const r382 = data.low20 + (range * 0.382); const r500 = data.low20 + (range * 0.5); const r618 = data.low20 + (range * 0.618); const s1 = data.low20; // Update Fib level displays in charts const fibElements = document.querySelectorAll('#multi-charts .fib-level, #multi-charts [class*="fib"]'); fibElements.forEach(el => { const text = el.textContent; if (text.includes('R:') || text.includes('100%')) { el.textContent = 'R: $' + r1.toFixed(2); } else if (text.includes('61.8%')) { el.textContent = '61.8%: $' + r618.toFixed(2); } else if (text.includes('50%')) { el.textContent = '50%: $' + r500.toFixed(2); } else if (text.includes('38.2%')) { el.textContent = '38.2%: $' + r382.toFixed(2); } else if (text.includes('S:') || text.includes('0%')) { el.textContent = 'S: $' + s1.toFixed(2); } }); console.log('Fib levels updated for ' + symbol + ': R=$' + r1.toFixed(2) + ', S=$' + s1.toFixed(2)); } // Update bias indicators in Multi-Charts function updateMultiChartBias(symbol, data) { if (!data) return; // Determine bias based on price change const bias = data.change >= 0 ? 'BULLISH' : 'BEARISH'; const biasColor = data.change >= 0 ? '#22c55e' : '#ef4444'; // Update bias displays document.querySelectorAll('#multi-charts .bias-label').forEach(el => { el.textContent = bias; el.style.color = biasColor; }); // Update MTF bias summary if visible const mtfBiasElements = document.querySelectorAll('.mtf-summary .bias, .mtf-summary-item .bias'); mtfBiasElements.forEach(el => { el.textContent = bias; el.style.color = biasColor; }); } // Initialize Multi-Charts symbol change handlers function initMultiChartsSymbolChange() { const multiChartSymbol = document.getElementById('multiChartSymbol'); if (multiChartSymbol) { // Remove existing onchange to avoid duplicates multiChartSymbol.removeAttribute('onchange'); multiChartSymbol.addEventListener('change', async function() { const symbol = this.value; console.log('Multi-Charts: Symbol changed to ' + symbol); // Sync with header symbol dropdown const mainSelect = document.getElementById('symbolSelect'); if (mainSelect && mainSelect.value !== symbol) { mainSelect.value = symbol; } // Update global currentSymbol currentSymbol = symbol; // Update Multi-Charts with new symbol await updateMultiCharts(symbol); // Also trigger full symbol change to update other tabs await changeSymbol(); }); } // Also listen for header symbol changes to update Multi-Charts const headerSymbol = document.getElementById('symbolSelect'); if (headerSymbol) { headerSymbol.addEventListener('change', function() { const symbol = this.value; // Update Multi-Charts dropdown to match if (multiChartSymbol && multiChartSymbol.value !== symbol) { multiChartSymbol.value = symbol; } // updateMultiCharts will be called via changeSymbol() }); } console.log('Multi-Charts symbol change handlers initialized'); } async function updateChart(panelType) { const symbol = document.getElementById('multiChartSymbol')?.value || 'SPY'; const timeframe = document.getElementById(`${panelType}Timeframe`)?.value; const chartType = document.getElementById(`${panelType}ChartType`)?.value; const container = document.getElementById(`${panelType}ChartContainer`); const loading = document.getElementById(`${panelType}Loading`); const settings = getMTFSettings(panelType); if (loading) loading.style.display = 'block'; try { let data = getMultiChartData(panelType, timeframe); mtfChartData[panelType] = data; if (!data || data.length === 0) { if (loading) loading.textContent = 'No data available'; return; } if (chartType === 'heikinashi') { data = convertToHeikinAshi(data); } if (chartType === 'pf') { renderEnhancedPF(panelType, mtfChartData[panelType], container, settings); document.getElementById(`${panelType}Legend`).style.display = 'none'; } else { renderLWChart(panelType, data, chartType, container, settings); document.getElementById(`${panelType}Legend`).style.display = 'flex'; } // Update price display const lastBar = data[data.length - 1]; const priceEl = document.getElementById(`${panelType}Price`); if (priceEl && lastBar) { priceEl.textContent = `$${lastBar.close.toFixed(2)}`; priceEl.className = lastBar.close >= lastBar.open ? 'price' : 'price down'; } // Update signal and bias const signal = lastBar.close > (data[data.length - 2]?.close || lastBar.close) ? 'CALL' : 'PUT'; updateMTFSignal(panelType, signal); // Calculate and display Fibonacci levels if (settings.showFib && chartType !== 'pf') { displayMTFFib(panelType, data); } else { document.getElementById(`${panelType}Fib`).innerHTML = ''; } if (loading) loading.style.display = 'none'; } catch (error) { console.error(`Error updating ${panelType} chart:`, error); if (loading) loading.textContent = 'Error loading chart'; } } function updateMTFSignal(panel, signal) { const signalEl = document.getElementById(`${panel}Signal`); const biasEl = document.getElementById(`${panel}Bias`); if (signalEl) { signalEl.textContent = signal; signalEl.className = `signal-badge-mtf ${signal.toLowerCase()}`; } if (biasEl) { const bias = signal === 'CALL' ? 'BULLISH' : 'BEARISH'; biasEl.textContent = bias; biasEl.className = `value ${bias.toLowerCase()}`; } // Update overall confluence const intraday = document.getElementById('intradaySignal')?.textContent || 'HOLD'; const swing = document.getElementById('swingSignal')?.textContent || 'HOLD'; const position = document.getElementById('positionSignal')?.textContent || 'HOLD'; let bullish = 0; [intraday, swing, position].forEach(s => { if (s === 'CALL') bullish++; }); const overallEl = document.getElementById('overallBias'); if (overallEl) { overallEl.textContent = `${bullish}/3 ${bullish >= 2 ? 'BULLISH' : 'BEARISH'}`; overallEl.className = `value ${bullish >= 2 ? 'bullish' : 'bearish'}`; } } function displayMTFFib(panelType, data) { const high = Math.max(...data.map(d => d.high)); const low = Math.min(...data.map(d => d.low)); const range = high - low; const fib = { high: high, low: low, level382: high - range * 0.382, level618: high - range * 0.618 }; const fibEl = document.getElementById(`${panelType}Fib`); if (fibEl) { fibEl.innerHTML = `
    R: $${fib.high.toFixed(2)}
    38.2%: $${fib.level382.toFixed(2)}
    61.8%: $${fib.level618.toFixed(2)}
    S: $${fib.low.toFixed(2)}
    `; } } function renderLWChart(panelType, data, chartType, container, settings) { // Clear existing chart if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; // Check if LightweightCharts is available if (typeof LightweightCharts === 'undefined') { console.warn('LightweightCharts not loaded, falling back to basic chart'); renderFallbackChart(panelType, data, chartType, container); return; } const chart = LightweightCharts.createChart(container, { width: container.clientWidth, height: container.clientHeight || 350, layout: { background: { color: '#ffffff' }, textColor: '#333' }, grid: { vertLines: { color: '#f0f0f0' }, horzLines: { color: '#f0f0f0' } }, crosshair: { mode: LightweightCharts.CrosshairMode.Normal }, rightPriceScale: { borderColor: '#e0e0e0' }, timeScale: { borderColor: '#e0e0e0', timeVisible: true } }); lwChartInstances[panelType] = chart; // Convert data to LightweightCharts format const lwData = data.map((bar, i) => ({ time: Math.floor(Date.now() / 1000) - (data.length - i) * 86400, open: bar.open, high: bar.high, low: bar.low, close: bar.close })); let mainSeries; if (chartType === 'line') { mainSeries = chart.addLineSeries({ color: '#2962FF', lineWidth: 2 }); mainSeries.setData(lwData.map(d => ({ time: d.time, value: d.close }))); } else { mainSeries = chart.addCandlestickSeries({ upColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', downColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350', borderVisible: false, wickUpColor: chartType === 'heikinashi' ? '#4caf50' : '#26a69a', wickDownColor: chartType === 'heikinashi' ? '#f44336' : '#ef5350' }); mainSeries.setData(lwData); } // Add EMAs if enabled if (settings.showEMA) { const ema9 = calculateEMAForLW(data, 9); const ema21 = calculateEMAForLW(data, 21); const ema50 = calculateEMAForLW(data, 50); const ema9Data = ema9.map((val, i) => ({ time: lwData[i].time, value: val })); const ema21Data = ema21.map((val, i) => ({ time: lwData[i].time, value: val })); const ema50Data = ema50.map((val, i) => ({ time: lwData[i].time, value: val })); chart.addLineSeries({ color: '#f59e0b', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema9Data); chart.addLineSeries({ color: '#3b82f6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema21Data); chart.addLineSeries({ color: '#8b5cf6', lineWidth: 1, crosshairMarkerVisible: false, priceLineVisible: false, lastValueVisible: false }).setData(ema50Data); } // Add volume if enabled - displayed in bottom 25% of chart if (settings.showVolume) { const volSeries = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: 'volume', scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.75, bottom: 0 } }); volSeries.setData(lwData.map((d, i) => ({ time: d.time, value: data[i].volume || 1000000, color: d.close >= d.open ? 'rgba(38,166,154,0.6)' : 'rgba(239,83,80,0.6)' }))); } chart.timeScale().fitContent(); } function calculateEMAForLW(data, period) { const k = 2 / (period + 1); const result = []; let ema = data[0]?.close || 0; for (let i = 0; i < data.length; i++) { ema = i === 0 ? data[i].close : data[i].close * k + ema * (1 - k); result.push(ema); } return result; } function renderFallbackChart(panelType, data, chartType, container) { // Create canvas for fallback container.innerHTML = ''; const canvas = container.querySelector('canvas'); renderMultiCandlestickChart(panelType, data, chartType, canvas); } // P&F Pattern Detection function detectPFPatterns(columns) { if (columns.length < 3) return { pattern: null, signal: 'HOLD' }; const patterns = []; const len = columns.length; const c1 = columns[len - 1]; const c3 = len >= 3 ? columns[len - 3] : null; const c5 = len >= 5 ? columns[len - 5] : null; if (c3 && c1.type === 'X' && c3.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); if (c1High > c3High) { patterns.push({ name: 'Double Top Breakout', type: 'bullish', strength: 85 }); } } if (c3 && c1.type === 'O' && c3.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); if (c1Low < c3Low) { patterns.push({ name: 'Double Bottom Breakdown', type: 'bearish', strength: 85 }); } } if (c5 && c1.type === 'X' && c3.type === 'X' && c5.type === 'X') { const c1High = Math.max(...c1.boxes); const c3High = Math.max(...c3.boxes); const c5High = Math.max(...c5.boxes); if (c1High > c3High && c1High > c5High) { patterns.push({ name: 'Triple Top Breakout', type: 'bullish', strength: 95 }); } } if (c5 && c1.type === 'O' && c3.type === 'O' && c5.type === 'O') { const c1Low = Math.min(...c1.boxes); const c3Low = Math.min(...c3.boxes); const c5Low = Math.min(...c5.boxes); if (c1Low < c3Low && c1Low < c5Low) { patterns.push({ name: 'Triple Bottom Breakdown', type: 'bearish', strength: 95 }); } } if (patterns.length === 0) { patterns.push(c1.type === 'X' ? { name: 'Uptrend in Progress', type: 'bullish', strength: 60 } : { name: 'Downtrend in Progress', type: 'bearish', strength: 60 }); } const strongest = patterns.sort((a, b) => b.strength - a.strength)[0]; return { pattern: strongest, signal: strongest.type === 'bullish' ? 'CALL' : 'PUT' }; } function renderEnhancedPF(panelType, data, container, settings) { if (lwChartInstances[panelType]) { lwChartInstances[panelType].remove(); lwChartInstances[panelType] = null; } container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 400; canvas.height = container.clientHeight || 350; container.appendChild(canvas); const ctx = canvas.getContext('2d'); const width = canvas.width, height = canvas.height; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); if (!data || data.length < 10) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Not enough data for P&F', width/2, height/2); return; } // Calculate box size const avgPrice = data.reduce((s, b) => s + b.close, 0) / data.length; let boxSize = avgPrice < 5 ? 0.25 : avgPrice < 20 ? 0.5 : avgPrice < 100 ? 1 : avgPrice < 200 ? 2 : avgPrice < 500 ? 4 : 8; if (panelType === 'intraday') boxSize *= 0.5; if (panelType === 'position') boxSize *= 1.5; const columns = generatePFCols(data, boxSize, 3); const patternResult = detectPFPatterns(columns); // Display pattern alert const patternEl = document.getElementById(`${panelType}Pattern`); if (settings.showPatterns && patternResult.pattern) { patternEl.style.display = 'block'; patternEl.className = `pattern-alert-mtf ${patternResult.pattern.type}`; patternEl.innerHTML = `${patternResult.pattern.type === 'bullish' ? '🟢' : '🔴'} ${patternResult.pattern.name} Strength: ${patternResult.pattern.strength}%`; updateMTFSignal(panelType, patternResult.signal); } else { patternEl.style.display = 'none'; } if (columns.length === 0) { ctx.fillStyle = '#666'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('No P&F reversals', width/2, height/2); return; } // Find price range let minP = Infinity, maxP = -Infinity; columns.forEach(c => c.boxes.forEach(p => { minP = Math.min(minP, p); maxP = Math.max(maxP, p); })); minP -= boxSize * 2; maxP += boxSize * 2; const pad = { top: 40, right: 50, bottom: 20, left: 50 }; const cw = width - pad.left - pad.right; const ch = height - pad.top - pad.bottom; const maxCols = Math.min(columns.length, 35); const dispCols = columns.slice(-maxCols); const colW = Math.min(14, cw / dispCols.length); // Draw grid ctx.strokeStyle = '#f0f0f0'; ctx.lineWidth = 0.5; for (let p = minP; p <= maxP; p += boxSize) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(width - pad.right, y); ctx.stroke(); } // Draw price labels ctx.fillStyle = '#666'; ctx.font = '9px Arial'; ctx.textAlign = 'right'; const step = Math.max(1, Math.floor((maxP - minP) / boxSize / 10)) * boxSize; for (let p = Math.ceil(minP / step) * step; p <= maxP; p += step) { const y = pad.top + ch - ((p - minP) / (maxP - minP)) * ch; ctx.fillText(p.toFixed(0), pad.left - 5, y + 3); } // Draw X's and O's const fs = Math.max(8, Math.min(12, colW - 1)); ctx.font = `bold ${fs}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; dispCols.forEach((col, i) => { const x = pad.left + (i + 0.5) * colW; col.boxes.forEach(price => { const y = pad.top + ch - ((price - minP) / (maxP - minP)) * ch; ctx.fillStyle = col.type === 'X' ? '#26a69a' : '#ef5350'; ctx.fillText(col.type, x, y); }); }); // Title ctx.fillStyle = '#333'; ctx.font = 'bold 11px Arial'; ctx.textAlign = 'left'; ctx.fillText('Point & Figure Chart', pad.left, 15); ctx.font = '10px Arial'; ctx.fillStyle = '#666'; ctx.fillText(`Box: $${boxSize.toFixed(2)} | Rev: 3`, pad.left + 110, 15); // Pattern name if (settings.showPatterns && patternResult.pattern) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = patternResult.pattern.type === 'bullish' ? '#059669' : '#dc2626'; ctx.textAlign = 'center'; ctx.fillText(patternResult.pattern.name, width / 2, 30); } } function getMultiChartData(panelType, timeframe) { if (!currentData || currentData.length === 0) return []; let data = currentData.map(bar => ({ date: bar.Date, open: parseFloat(bar.Open) || 0, high: parseFloat(bar.High) || 0, low: parseFloat(bar.Low) || 0, close: parseFloat(bar.Close) || 0, volume: parseFloat(bar.Volume) || 0 })).filter(bar => bar.close > 0); if (panelType === 'intraday') { // Simulate intraday bars from daily data var minutes = 15; if (timeframe === '1m') minutes = 1; else if (timeframe === '5m') minutes = 5; else if (timeframe === '15m') minutes = 15; else if (timeframe === '30m') minutes = 30; else if (timeframe === '1h') minutes = 60; return simulateIntradayBars(data.slice(-5), minutes); } if (panelType === 'swing') { if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '4h') return simulate4HourBars(data.slice(-15)); if (timeframe === '1h') return simulate1HourBars(data.slice(-10)); return data.slice(-60); // Daily } if (panelType === 'position') { if (timeframe === '3M' || timeframe === 'Q') return aggregateQuarterly(data); if (timeframe === '1w' || timeframe === 'W') return aggregateWeekly(data); if (timeframe === '1M') return aggregateMonthly(data); return data.slice(-90); // Daily } return data; } function simulate4HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 6; i++) { var range = day.high - day.low; var variance = range / 6; var basePrice = day.open + (day.close - day.open) * (i / 6); bars.push({ date: day.date + ' ' + (9 + i * 4) + ':00', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 6) }); } }); return bars.slice(-60); } function simulate1HourBars(dailyData) { var bars = []; dailyData.forEach(function(day) { for (var i = 0; i < 7; i++) { var range = day.high - day.low; var variance = range / 7; var basePrice = day.open + (day.close - day.open) * (i / 7); bars.push({ date: day.date + ' ' + (9 + i) + ':30', open: basePrice + (Math.random() - 0.5) * variance, high: basePrice + Math.random() * variance, low: basePrice - Math.random() * variance, close: basePrice + (Math.random() - 0.5) * variance, volume: Math.floor(day.volume / 7) }); } }); return bars.slice(-60); } function simulateIntradayBars(dailyData, minutes) { const intradayBars = []; const barsPerDay = Math.floor(390 / minutes); dailyData.forEach(day => { const range = day.high - day.low; for (let i = 0; i < barsPerDay; i++) { const progress = i / barsPerDay; const basePrice = day.open + (day.close - day.open) * progress; const noise = (Math.random() - 0.5) * range * 0.3; const open = i === 0 ? day.open : intradayBars[intradayBars.length - 1]?.close || basePrice; const close = basePrice + noise; intradayBars.push({ date: day.date, time: `${Math.floor(9.5 + (i * minutes) / 60)}:${String((30 + i * minutes) % 60).padStart(2, '0')}`, open: open, high: Math.max(open, close) + Math.random() * range * 0.1, low: Math.min(open, close) - Math.random() * range * 0.1, close: close, volume: day.volume / barsPerDay }); } }); return intradayBars.slice(-50); } function aggregateWeekly(dailyData) { const weeks = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); const weekKey = weekStart.toISOString().split('T')[0]; if (!weeks[weekKey]) { weeks[weekKey] = { date: weekKey, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { weeks[weekKey].high = Math.max(weeks[weekKey].high, bar.high); weeks[weekKey].low = Math.min(weeks[weekKey].low, bar.low); weeks[weekKey].close = bar.close; weeks[weekKey].volume += bar.volume; } }); return Object.values(weeks).slice(-30); } function aggregateMonthly(dailyData) { const months = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!months[monthKey]) { months[monthKey] = { date: monthKey + '-01', open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { months[monthKey].high = Math.max(months[monthKey].high, bar.high); months[monthKey].low = Math.min(months[monthKey].low, bar.low); months[monthKey].close = bar.close; months[monthKey].volume += bar.volume; } }); return Object.values(months).slice(-24); } function aggregateQuarterly(dailyData) { const quarters = {}; dailyData.forEach(bar => { const date = new Date(bar.date); const quarter = Math.floor(date.getMonth() / 3) + 1; const quarterKey = `${date.getFullYear()}-Q${quarter}`; if (!quarters[quarterKey]) { quarters[quarterKey] = { date: `${date.getFullYear()}-${String((quarter - 1) * 3 + 1).padStart(2, '0')}-01`, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume }; } else { quarters[quarterKey].high = Math.max(quarters[quarterKey].high, bar.high); quarters[quarterKey].low = Math.min(quarters[quarterKey].low, bar.low); quarters[quarterKey].close = bar.close; quarters[quarterKey].volume += bar.volume; } }); return Object.values(quarters).slice(-12); } function convertToHeikinAshi(data) { if (!data || data.length === 0) return []; const haData = []; data.forEach((bar, i) => { const haBar = { ...bar }; if (i === 0) { haBar.open = (bar.open + bar.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } else { const prevHA = haData[i - 1]; haBar.open = (prevHA.open + prevHA.close) / 2; haBar.close = (bar.open + bar.high + bar.low + bar.close) / 4; } haBar.high = Math.max(bar.high, haBar.open, haBar.close); haBar.low = Math.min(bar.low, haBar.open, haBar.close); haData.push(haBar); }); return haData; } function generatePFCols(data, boxSize, reversal) { if (!data || data.length === 0) return []; const columns = []; let currentCol = { type: 'X', boxes: [Math.floor(data[0].close / boxSize) * boxSize] }; data.forEach(bar => { const high = Math.floor(bar.high / boxSize) * boxSize; const low = Math.floor(bar.low / boxSize) * boxSize; const lastBox = currentCol.boxes[currentCol.boxes.length - 1]; if (currentCol.type === 'X') { if (high > lastBox) { for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } else if (lastBox - low >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'O', boxes: [] }; for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } } else { if (low < lastBox) { for (let p = lastBox - boxSize; p >= low; p -= boxSize) currentCol.boxes.push(p); } else if (high - lastBox >= reversal * boxSize) { columns.push(currentCol); currentCol = { type: 'X', boxes: [] }; for (let p = lastBox + boxSize; p <= high; p += boxSize) currentCol.boxes.push(p); } } }); if (currentCol.boxes.length > 0) columns.push(currentCol); return columns.slice(-30); } // Keep old function for fallback function renderMultiCandlestickChart(panelType, data, chartType, canvas) { const ctx = canvas.getContext('2d'); if (multiChartInstances && multiChartInstances[panelType]) { multiChartInstances[panelType].destroy(); } const labels = data.map((bar, i) => { if (bar.time) return bar.time; const dateStr = bar.date?.split('T')[0] || ''; return dateStr.slice(5); }); const upColor = chartType === 'heikinashi' ? '#4caf50' : '#26a69a'; const downColor = chartType === 'heikinashi' ? '#f44336' : '#ef5350'; if (typeof Chart !== 'undefined') { multiChartInstances[panelType] = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Price', data: data.map(bar => bar.close), backgroundColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderColor: data.map(bar => bar.close >= bar.open ? upColor : downColor), borderWidth: 1, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: true, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', maxTicksLimit: 8, font: { size: 9 } } }, y: { display: true, position: 'right', grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#787b86', font: { size: 9 } } } } } }); } } // Initialize multi-charts when tab is clicked document.addEventListener('DOMContentLoaded', function() { // Initialize symbol change handlers initMultiChartsSymbolChange(); const multiChartsTab = document.querySelector('[data-tab="multi-charts"]'); if (multiChartsTab) { multiChartsTab.addEventListener('click', function() { setTimeout(initMultiCharts, 100); }); } }); // ============================================ // DAILY ANALYSIS SYSTEM // ============================================ let currentAnalysisMode = 'morning'; let dailyAnalysisData = null; // Initialize Daily Analysis when tab is clicked document.addEventListener('DOMContentLoaded', function() { const analysisTab = document.querySelector('[data-tab="daily-analysis"]'); if (analysisTab) { analysisTab.addEventListener('click', async function() { await loadRealAgentSignals(); setTimeout(generateDailyReport, 100); }); } // Pre-load agent signals on page load loadRealAgentSignals(); // Auto-generate reports at scheduled times checkScheduledAnalysis(); setInterval(checkScheduledAnalysis, 60000); // Check every minute }); function checkScheduledAnalysis() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Morning analysis at 9:35 AM if (hours === 9 && minutes === 35) { currentAnalysisMode = 'morning'; generateDailyReport(); } // Evening analysis at 6:00 PM if (hours === 18 && minutes === 0) { currentAnalysisMode = 'evening'; generateDailyReport(); } } function switchAnalysisMode(mode) { currentAnalysisMode = mode; // Update button states document.getElementById('morningBtn')?.classList.toggle('active', mode === 'morning'); document.getElementById('eveningBtn')?.classList.toggle('active', mode === 'evening'); // Update title const planTitle = document.getElementById('planTitle'); if (planTitle) { planTitle.textContent = mode === 'morning' ? '☀️ Morning Trading Plan' : '🌙 Evening Review & Next Day Plan'; } generateDailyReport(); } function refreshAnalysis() { generateDailyReport(); } // AI Quick Analysis for Overview Tab function refreshOverviewAI() { updateOverviewAiAnalysis(); } function updateOverviewAiAnalysis() { const analysisEl = document.getElementById('overviewAiAnalysis'); if (!analysisEl) return; if (!currentData || currentData.length < 2) { analysisEl.innerHTML = '
    Loading data...
    '; return; } const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(prevBar.Close) || price; const change = price - prevClose; const changePct = ((change / prevClose) * 100).toFixed(2); const atr = parseFloat(lastBar.ATR) || price * 0.015; const bias = lastBar.Bias || 'NEUTRAL'; const volume = parseInt(lastBar.Volume) || 0; // Calculate 20-day average volume const recentData = currentData.slice(-20); const avgVol = recentData.reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / recentData.length; const volRatio = avgVol > 0 ? (volume / avgVol * 100).toFixed(0) : 100; // Determine signal strength const fastSMA = parseFloat(lastBar.FastSMA) || price; const slowSMA = parseFloat(lastBar.SlowSMA) || price; const priceVsFast = price > fastSMA ? 'above' : 'below'; const fastVsSlow = fastSMA > slowSMA ? 'bullish' : 'bearish'; // Build AI analysis const direction = bias === 'BULLISH' ? 'bullish' : bias === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = bias === 'BULLISH' ? 'CALL' : bias === 'BEARISH' ? 'PUT' : 'neutral'; const momentum = change > 0 ? 'positive' : 'negative'; // Calculate key levels const entryZoneLow = (price - atr * 0.3).toFixed(2); const entryZoneHigh = bias === 'BULLISH' ? price.toFixed(2) : (price + atr * 0.3).toFixed(2); const entryZone = bias === 'BULLISH' ? '$' + entryZoneLow + ' - $' + price.toFixed(2) : '$' + price.toFixed(2) + ' - $' + (price + atr * 0.3).toFixed(2); const stopLoss = bias === 'BULLISH' ? (price - atr * 0.75).toFixed(2) : (price + atr * 0.75).toFixed(2); const target1 = bias === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = bias === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Confidence score let confidence = 50; if (bias === 'BULLISH' && price > fastSMA) confidence += 15; if (bias === 'BEARISH' && price < fastSMA) confidence += 15; if (parseInt(volRatio) > 100) confidence += 10; if (Math.abs(parseFloat(changePct)) > 0.5) confidence += 5; confidence = Math.min(95, confidence); const confColor = confidence >= 75 ? 'var(--success)' : confidence >= 60 ? 'var(--warning)' : 'var(--danger)'; const signalColor = bias === 'BULLISH' ? 'var(--success)' : bias === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; const volMsg = parseInt(volRatio) > 120 ? 'High volume confirms the move.' : parseInt(volRatio) < 80 ? 'Low volume - move may lack conviction.' : 'Normal volume activity.'; const confMsg = confidence >= 75 ? 'High confidence - favorable setup' : confidence >= 60 ? 'Moderate confidence - smaller size' : 'Low confidence - consider waiting'; const dteText = tradingMode === 'INTRADAY' ? '0-1 days' : '3-7 days'; analysisEl.innerHTML = `
    Market Snapshot
    ${currentSymbol} is showing ${direction} bias with ${parseFloat(changePct) > 0 ? '+' : ''}${changePct}% move today.
    Price is ${priceVsFast} the 9-SMA with ${fastVsSlow} SMA crossover.
    Trade Setup
    Signal: ${optionType}
    Entry Zone: ${entryZone}
    Stop Loss: $${stopLoss}
    Target 1: $${target1}
    Target 2: $${target2}
    Volume Analysis
    Today's volume is at ${volRatio}% of 20-day average.
    ${volMsg}
    Confidence
    ${confidence}%
    ${confMsg}
    ATR: $${atr.toFixed(2)} | Risk/Reward: 1:2 minimum | DTE: ${dteText}
    `; } // Data Pattern Detection with Dates function updateDataPatternDetection() { const detectionEl = document.getElementById('dataPatternDetection'); if (!detectionEl) return; if (!currentData || currentData.length < 10) { detectionEl.innerHTML = '
    Insufficient data for pattern detection
    '; return; } // Format date as "Mon DD" (e.g., "Dec 2", "Nov 27") function formatDateShort(dateStr) { const date = new Date(dateStr); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months[date.getMonth()] + ' ' + date.getDate(); } // Get last 30 days of data for analysis const recentData = currentData.slice(-30); const analysisResults = []; // 1. UPTREND DETECTION - Check last 5 days for consecutive higher closes const last5Days = recentData.slice(-5); let uptrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) > parseFloat(last5Days[i-1].Close)) { uptrendDays++; } } let downtrendDays = 0; for (let i = 1; i < last5Days.length; i++) { if (parseFloat(last5Days[i].Close) < parseFloat(last5Days[i-1].Close)) { downtrendDays++; } } if (uptrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'uptrend', icon: '📈', text: `UPTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${uptrendDays} of 4 days with higher closes` }); } else if (downtrendDays >= 3) { const startDate = formatDateShort(last5Days[0].Date || last5Days[0].date); const endDate = formatDateShort(last5Days[last5Days.length - 1].Date || last5Days[last5Days.length - 1].date); analysisResults.push({ type: 'downtrend', icon: '📉', text: `DOWNTREND detected in last 5 days (${startDate} - ${endDate})`, detail: `${downtrendDays} of 4 days with lower closes` }); } // 2. HIGH VOLUME DAYS - Find days with >150% of 20-day average volume const avgVolume = recentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const highVolumeDays = []; recentData.forEach(bar => { const vol = parseInt(bar.Volume) || 0; if (vol > avgVolume * 1.5) { highVolumeDays.push({ date: formatDateShort(bar.Date || bar.date), volume: vol, ratio: ((vol / avgVolume) * 100).toFixed(0) }); } }); if (highVolumeDays.length > 0) { // Sort by most recent (reverse chronological) highVolumeDays.reverse(); const displayDates = highVolumeDays.slice(0, 5).map(d => d.date); const moreCount = highVolumeDays.length > 5 ? ` (+${highVolumeDays.length - 5} more)` : ''; analysisResults.push({ type: 'highVolume', icon: '🔊', text: `${highVolumeDays.length} HIGH VOLUME days (>150% of average)`, detail: displayDates.join(', ') + moreCount }); } // 3. GAP UPs - Find days where Open > Previous Close const gapUps = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = currOpen - prevClose; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapUps.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapUps.length > 0) { // Sort by gap size (largest first) gapUps.sort((a, b) => b.gap - a.gap); const top3 = gapUps.slice(0, 3); const detailLines = top3.map(g => `${g.date}: +$${g.gap.toFixed(2)} gap`); const moreCount = gapUps.length > 3 ? `
    (showing top 3 by size, +${gapUps.length - 3} more)` : ''; analysisResults.push({ type: 'gapUp', icon: '⬆️', text: `${gapUps.length} GAP UP(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // 4. GAP DOWNs - Find days where Open < Previous Close const gapDowns = []; for (let i = 1; i < recentData.length; i++) { const prevClose = parseFloat(recentData[i-1].Close) || 0; const currOpen = parseFloat(recentData[i].Open) || 0; const gapSize = prevClose - currOpen; if (gapSize > 0 && gapSize >= prevClose * 0.002) { // At least 0.2% gap gapDowns.push({ date: formatDateShort(recentData[i].Date || recentData[i].date), gap: gapSize, pct: ((gapSize / prevClose) * 100).toFixed(2) }); } } if (gapDowns.length > 0) { // Sort by gap size (largest first) gapDowns.sort((a, b) => b.gap - a.gap); const top3 = gapDowns.slice(0, 3); const detailLines = top3.map(g => `${g.date}: -$${g.gap.toFixed(2)} gap`); const moreCount = gapDowns.length > 3 ? `
    (showing top 3 by size, +${gapDowns.length - 3} more)` : ''; analysisResults.push({ type: 'gapDown', icon: '⬇️', text: `${gapDowns.length} GAP DOWN(s) detected`, detail: detailLines.join('
    ') + moreCount }); } // Build HTML output if (analysisResults.length === 0) { detectionEl.innerHTML = `
    No significant patterns detected in last 30 days
    `; return; } let html = '
    '; analysisResults.forEach(result => { const bgColor = result.type === 'uptrend' ? 'rgba(34,197,94,0.1)' : result.type === 'downtrend' ? 'rgba(239,68,68,0.1)' : result.type === 'highVolume' ? 'rgba(139,92,246,0.1)' : result.type === 'gapUp' ? 'rgba(34,197,94,0.08)' : 'rgba(239,68,68,0.08)'; html += `
    ${result.icon} ${result.text}
    ${result.detail}
    `; }); html += '
    '; detectionEl.innerHTML = html; } // ============================================ // GANN ANALYSIS FUNCTIONS // ============================================ // Gann Quotes for rotation const gannQuotes = [ '"Time is more important than price." - W.D. Gann', '"The future is but a repetition of the past." - W.D. Gann', '"When time and price balance, change is imminent." - W.D. Gann', '"Every movement in the market is a result of natural law." - W.D. Gann', '"The market is never wrong, opinions often are." - W.D. Gann', '"Knowledge is the key to success in speculation." - W.D. Gann', '"Never trade without stop losses." - W.D. Gann', '"Buy and sell on definite rules, not on hope or fear." - W.D. Gann' ]; // Complete ETF Data - ACCURATE PRICES as of Dec 11, 2025 const symbolGannData = { SPY: { price: 689.12, low: 479.05, high: 689.70, lowDate: 'Oct 27', highDate: 'Dec 11', sector: 'S&P 500', atr: 6.5, sacredLevels: [576, 600, 625, 650, 676, 700] }, QQQ: { price: 525.80, low: 410.50, high: 530.25, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Nasdaq 100', atr: 8.2, sacredLevels: [500, 525, 550, 576] }, IWM: { price: 234.50, low: 195.25, high: 244.80, lowDate: 'Oct 27', highDate: 'Nov 25', sector: 'Small Cap', atr: 4.1, sacredLevels: [225, 234, 244, 256] }, XLE: { price: 45.51, low: 36.63, high: 46.66, lowDate: 'Sep 10', highDate: 'Dec 11', sector: 'Energy', atr: 0.9, sacredLevels: [40, 45, 49, 52] }, XLF: { price: 50.85, low: 38.50, high: 52.20, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Financials', atr: 0.9, sacredLevels: [45, 49, 52, 56] }, XLK: { price: 235.60, low: 185.30, high: 240.50, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Technology', atr: 4.5, sacredLevels: [216, 225, 240, 256] }, XLV: { price: 145.20, low: 128.50, high: 148.75, lowDate: 'Nov 1', highDate: 'Dec 4', sector: 'Healthcare', atr: 2.1, sacredLevels: [136, 144, 149, 156] }, XLI: { price: 138.90, low: 115.25, high: 142.30, lowDate: 'Oct 27', highDate: 'Dec 5', sector: 'Industrials', atr: 2.3, sacredLevels: [125, 133, 141, 150] }, XLB: { price: 92.15, low: 78.40, high: 95.60, lowDate: 'Nov 10', highDate: 'Dec 2', sector: 'Materials', atr: 1.6, sacredLevels: [81, 90, 96, 103] }, XLU: { price: 78.50, low: 65.20, high: 82.15, lowDate: 'Oct 15', highDate: 'Nov 28', sector: 'Utilities', atr: 1.2, sacredLevels: [72, 79, 84, 90] }, XLP: { price: 82.30, low: 72.80, high: 85.40, lowDate: 'Oct 20', highDate: 'Dec 1', sector: 'Consumer Staples', atr: 1.0, sacredLevels: [74, 81, 86, 92] }, XLY: { price: 225.45, low: 175.60, high: 232.80, lowDate: 'Oct 27', highDate: 'Dec 6', sector: 'Consumer Disc', atr: 4.2, sacredLevels: [196, 216, 233, 250] } }; // Calculate Sacred Numbers (Gann's key numbers) function calculateSacredNumbers(price) { if (!price || price <= 0) return []; const sacredNumbers = [7, 9, 12, 36, 52, 90, 144, 180, 270, 360]; const results = []; sacredNumbers.forEach(num => { // Calculate multiples of sacred numbers near current price const multiple = Math.round(price / num); const nearestSacred = multiple * num; const diff = nearestSacred - price; const diffPct = (diff / price) * 100; results.push({ number: num, nearestLevel: nearestSacred, diff: diff.toFixed(2), diffPct: diffPct.toFixed(2), isNear: Math.abs(diffPct) < 2 }); }); return results; } // Get symbol-specific Gann analysis data function getSymbolGannData(symbol) { return symbolGannData[symbol] || symbolGannData['SPY']; } // Update Gann Analysis for specific symbol function updateGannAnalysisForSymbol(symbol) { console.log('Updating ALL Gann sections for: ' + symbol); const data = getSymbolGannData(symbol); if (!data) { console.log('No data for symbol: ' + symbol); return; } const price = data.price; // Update header with symbol info const symbolEl = document.getElementById('gannCurrentSymbol'); const priceEl = document.getElementById('gannCurrentPrice'); const sectorEl = document.getElementById('gannSectorName'); const turnSymbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol; if (priceEl) priceEl.textContent = '$' + price.toFixed(2); if (sectorEl) sectorEl.textContent = '(' + data.sector + ')'; if (turnSymbolEl) turnSymbolEl.textContent = symbol; // Calculate and update Square of 9 const sq9Levels = calculateSquareOf9(price); updateSquareOf9Display(sq9Levels, symbol); // Calculate days since pivots (estimate based on dates) const today = new Date(); const daysSinceLow = 45; // Approximate days since Oct/Nov low const daysSinceHigh = 5; // Approximate days since Dec high // Calculate Gann Angles from pivots using new data structure const anglesFromLow = calculateGannAnglesFromPivot(data.low, daysSinceLow, price); const anglesFromHigh = calculateGannAnglesFromPivot(data.high, daysSinceHigh, price); updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol); // Calculate Sacred Numbers const sacredNums = calculateSacredNumbers(price); updateSacredNumbersDisplay(sacredNums, symbol); // Update pivot info with new data structure updatePivotInfo(data, symbol); // Recalculate confluence score const cycles = calculateGannCycles(); const allAngles = [...anglesFromLow, ...anglesFromHigh]; const confluence = calculateGannConfluence(sq9Levels, cycles, allAngles, price); updateConfluenceDisplay(confluence); // CRITICAL: Also update Gann Forecast section updateGannForecast(symbol); console.log('Gann Analysis complete for ' + symbol + ' at $' + price.toFixed(2)); } // Update Gann Forecast for symbol function updateGannForecast(symbol) { const data = symbolGannData[symbol]; if (!data) return; const price = data.price; const sq9 = calculateSquareOf9(price); // Determine bias based on price position relative to high/low midpoint const midPoint = (data.high + data.low) / 2; const isBullish = price > midPoint; // Update Gann Directional Bias - use gannBiasText (the actual text element) const biasTextEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); if (biasTextEl) { biasTextEl.textContent = isBullish ? 'BULLISH' : 'BEARISH'; biasTextEl.style.color = isBullish ? '#22c55e' : '#ef4444'; } if (biasContainerEl) { biasContainerEl.style.background = isBullish ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; } // Get support and resistance from sq9 const support45 = sq9 && sq9.support && sq9.support[0] ? sq9.support[0].price : price * 0.98; const resistance45 = sq9 && sq9.resistance && sq9.resistance[0] ? sq9.resistance[0].price : price * 1.02; // Update Next Support (Sq9) - use forecastSupport const supportEl = document.getElementById('forecastSupport'); if (supportEl) { supportEl.textContent = '$' + support45.toFixed(2); } // Update Next Resistance (Sq9) - use forecastResistance const resistanceEl = document.getElementById('forecastResistance'); if (resistanceEl) { resistanceEl.textContent = '$' + resistance45.toFixed(2); } // Calculate next cycle turn (Dec 21, 2025 Winter Solstice) const today = new Date(); const nextTurn = new Date(2025, 11, 21); // Dec 21, 2025 const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); // Update Next Cycle Turn - use forecastCycleDate const turnDateEl = document.getElementById('forecastCycleDate'); if (turnDateEl) { turnDateEl.textContent = 'Dec 21, 2025'; } // Update Days Until - use forecastDaysUntil const daysEl = document.getElementById('forecastDaysUntil'); if (daysEl) { daysEl.textContent = daysUntil + ' days'; } // Update Projected Move - SYMBOL SPECIFIC - use projectedMoveText const projectedMove = isBullish ? (resistance45 - price).toFixed(2) : (price - support45).toFixed(2); const targetPrice = isBullish ? resistance45 : support45; const projectedEl = document.getElementById('projectedMoveText'); const projectedContainer = document.getElementById('projectedMove'); if (projectedEl) { if (isBullish) { projectedEl.innerHTML = 'Expect +$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#22c55e'; } else { projectedEl.innerHTML = 'Expect -$' + projectedMove + ' move within 7 days to $' + targetPrice.toFixed(2) + ''; projectedEl.style.color = '#ef4444'; } } if (projectedContainer) { if (isBullish) { projectedContainer.style.background = 'linear-gradient(135deg, rgba(34,197,94,0.15), rgba(34,197,94,0.05))'; projectedContainer.style.borderColor = 'rgba(34,197,94,0.3)'; } else { projectedContainer.style.background = 'linear-gradient(135deg, rgba(239,68,68,0.15), rgba(239,68,68,0.05))'; projectedContainer.style.borderColor = 'rgba(239,68,68,0.3)'; } } console.log('Gann Forecast updated for ' + symbol + ': ' + (isBullish ? 'BULLISH' : 'BEARISH')); } // Calculate Gann angles from a pivot point function calculateGannAnglesFromPivot(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; const scaleFactor = pivotPrice * 0.001; return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 }; }); } // Update Square of 9 display for symbol function updateSquareOf9Display(levels, symbol) { const supportEl = document.getElementById('sq9Support'); const resistanceEl = document.getElementById('sq9Resistance'); if (!levels) return; // Update support levels - handle both object and array formats if (supportEl && levels.support) { var supportArr = Array.isArray(levels.support) ? levels.support : Object.entries(levels.support).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); supportEl.innerHTML = supportArr.slice(0, 4).map(function(level) { return '
    -' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } // Update resistance levels - handle both object and array formats if (resistanceEl && levels.resistance) { var resistArr = Array.isArray(levels.resistance) ? levels.resistance : Object.entries(levels.resistance).map(function([key, val]) { var angle = key.replace('deg', ''); var price = typeof val === 'object' ? val.price : val; return { angle: angle, price: price }; }); resistanceEl.innerHTML = resistArr.slice(0, 4).map(function(level) { return '
    +' + level.angle + '°:$' + (typeof level.price === 'number' ? level.price.toFixed(2) : level.price) + '
    '; }).join(''); } } // Update Gann Angles display function updateGannAnglesDisplay(anglesFromLow, anglesFromHigh, symbol) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); // Update angles from low (bullish) if (lowEl && anglesFromLow.length > 0) { const keyAngles = anglesFromLow.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); lowEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update angles from high (bearish) if (highEl && anglesFromHigh.length > 0) { const keyAngles = anglesFromHigh.filter(a => ['1x1', '2x1', '4x1', '1x2', '1x4', '8x1'].includes(a.name)); highEl.innerHTML = keyAngles.slice(0, 6).map(angle => `
    ${angle.name}
    $${angle.price.toFixed(2)}
    `).join(''); } // Update position text if (positionEl) { const nearAngles = [...anglesFromLow, ...anglesFromHigh].filter(a => a.isNear); if (nearAngles.length > 0) { const nearest = nearAngles[0]; positionEl.textContent = `On ${nearest.name} angle - Key Level`; } else { const aboveLow = anglesFromLow.filter(a => a.isAbove); if (aboveLow.length > 0) { positionEl.textContent = `Above ${aboveLow[aboveLow.length - 1].name} from Low - Bullish`; } else { positionEl.textContent = `Below key angles - Cautious`; } } } } // Update Sacred Numbers display function updateSacredNumbersDisplay(sacredNums, symbol) { const container = document.getElementById('sacredNumbers'); if (!container) return; // Filter to the most relevant sacred numbers (7, 9, 12, 52, 144) const keyNumbers = sacredNums.filter(n => [7, 9, 12, 52, 144].includes(n.number)); container.innerHTML = keyNumbers.slice(0, 5).map(num => `
    ${num.number}
    $${num.nearestLevel.toFixed(2)}
    `).join(''); } // Update Pivot Info display function updatePivotInfo(data, symbol) { const pivotLowEl = document.getElementById('gannPivotLow'); const pivotHighEl = document.getElementById('gannPivotHigh'); if (pivotLowEl) { pivotLowEl.textContent = '$' + data.low.toFixed(2) + ' (' + data.lowDate + ')'; } if (pivotHighEl) { pivotHighEl.textContent = '$' + data.high.toFixed(2) + ' (' + data.highDate + ')'; } } // Calculate Square of 9 levels function calculateSquareOf9(price) { if (!price || price <= 0) return null; const sqrt = Math.sqrt(price); const angles = [45, 90, 135, 180, 225, 270, 315, 360]; const support = []; const resistance = []; angles.forEach(angle => { // Resistance levels (adding to sqrt) const resistSqrt = sqrt + (angle / 180); const resistPrice = resistSqrt * resistSqrt; resistance.push({ angle: angle, price: resistPrice, diff: resistPrice - price, diffPct: ((resistPrice - price) / price * 100).toFixed(2) }); // Support levels (subtracting from sqrt) const supportSqrt = sqrt - (angle / 180); if (supportSqrt > 0) { const supportPrice = supportSqrt * supportSqrt; support.push({ angle: angle, price: supportPrice, diff: price - supportPrice, diffPct: ((price - supportPrice) / price * 100).toFixed(2) }); } }); return { support, resistance, sqrt, price }; } // Calculate Gann Time Cycles function calculateGannCycles(referenceDate) { const today = new Date(); const ref = referenceDate ? new Date(referenceDate) : new Date(today.getFullYear(), 0, 1); // Start of year const daysSinceRef = Math.floor((today - ref) / (1000 * 60 * 60 * 24)); const cycles = [ { name: '30-Day Minor', days: 30, color: '#3b82f6' }, { name: '45-Day (1/8 yr)', days: 45, color: '#8b5cf6' }, { name: '60-Day', days: 60, color: '#06b6d4' }, { name: '90-Day (Qtr)', days: 90, color: '#10b981' }, { name: '120-Day (1/3 yr)', days: 120, color: '#f59e0b' }, { name: '144-Day Sacred', days: 144, color: '#d4a017' }, { name: '180-Day (Semi)', days: 180, color: '#ef4444' }, { name: '360-Day (Annual)', days: 360, color: '#ec4899' } ]; return cycles.map(cycle => { const position = daysSinceRef % cycle.days; const progress = (position / cycle.days) * 100; const daysUntilTurn = cycle.days - position; const nextTurnDate = new Date(today.getTime() + daysUntilTurn * 24 * 60 * 60 * 1000); const isNearTurn = daysUntilTurn <= 2 || position <= 2; return { ...cycle, position, progress, daysUntilTurn, nextTurnDate, isNearTurn }; }); } // Detect significant pivots from price data function detectGannPivots(data) { if (!data || data.length < 20) return { high: null, low: null }; const lookback = Math.min(60, data.length); const recentData = data.slice(-lookback); let highestHigh = { price: 0, date: null, index: 0 }; let lowestLow = { price: Infinity, date: null, index: 0 }; recentData.forEach((bar, i) => { const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || Infinity; const date = bar.Date || bar.date; if (high > highestHigh.price) { highestHigh = { price: high, date, index: i }; } if (low < lowestLow.price) { lowestLow = { price: low, date, index: i }; } }); return { high: highestHigh.price > 0 ? highestHigh : null, low: lowestLow.price < Infinity ? lowestLow : null, dataLength: lookback }; } // Calculate Gann Angles from pivot function calculateGannAngles(pivotPrice, daysSincePivot, currentPrice) { if (!pivotPrice || daysSincePivot < 1) return []; // Gann angle ratios: price units per time unit const angleRatios = [ { name: '1x8', ratio: 0.125, degrees: 7.5 }, { name: '1x4', ratio: 0.25, degrees: 15 }, { name: '1x3', ratio: 0.333, degrees: 18.75 }, { name: '1x2', ratio: 0.5, degrees: 26.25 }, { name: '1x1', ratio: 1, degrees: 45 }, { name: '2x1', ratio: 2, degrees: 63.75 }, { name: '3x1', ratio: 3, degrees: 71.25 }, { name: '4x1', ratio: 4, degrees: 75 }, { name: '8x1', ratio: 8, degrees: 82.5 } ]; // Scale factor for price movement (ATR-based or percentage) const scaleFactor = pivotPrice * 0.001; // 0.1% per day as base unit return angleRatios.map(angle => { const priceAtAngle = pivotPrice + (angle.ratio * scaleFactor * daysSincePivot); const diffFromCurrent = currentPrice - priceAtAngle; const isAbove = currentPrice > priceAtAngle; return { ...angle, price: priceAtAngle, diff: diffFromCurrent, isAbove, isNear: Math.abs(diffFromCurrent / currentPrice) < 0.005 // Within 0.5% }; }); } // Calculate Gann Confluence Score function calculateGannConfluence(sq9Levels, cycles, angles, price) { let score = 0; const factors = []; // Factor 1: Price near Square of 9 level (+25) let nearSq9 = false; if (sq9Levels) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const allLevels = [...supportVals, ...resistVals]; nearSq9 = allLevels.some(level => typeof level === 'object' ? Math.abs(level.diffPct) < 1.5 : false); } if (nearSq9) { score += 25; factors.push({ text: 'Price near Square of 9 level', points: 25, active: true }); } else { factors.push({ text: 'Price near Square of 9 level', points: 0, active: false }); } // Factor 2: Time near cycle turn (+25) const nearCycleTurn = cycles && cycles.some(c => c.isNearTurn); if (nearCycleTurn) { score += 25; factors.push({ text: 'Time near cycle turn date', points: 25, active: true }); } else { factors.push({ text: 'Time near cycle turn date', points: 0, active: false }); } // Factor 3: Price on Gann angle (+25) const onAngle = angles && angles.some(a => a.isNear); if (onAngle) { score += 25; factors.push({ text: 'Price on Gann angle', points: 25, active: true }); } else { factors.push({ text: 'Price on Gann angle', points: 0, active: false }); } // Factor 4: Multiple cycles converging (+25) const convergingCycles = cycles ? cycles.filter(c => c.daysUntilTurn <= 5).length : 0; if (convergingCycles >= 2) { score += 25; factors.push({ text: `${convergingCycles} cycles converging`, points: 25, active: true }); } else { factors.push({ text: 'Multiple cycles converging', points: 0, active: false }); } return { score, factors }; } // Get important Gann dates for current year function getGannDates() { const year = new Date().getFullYear(); const today = new Date(); const dates = [ { name: 'Spring Equinox', date: new Date(year, 2, 20), icon: '🌸' }, { name: 'Summer Solstice', date: new Date(year, 5, 21), icon: '☀️' }, { name: 'Fall Equinox', date: new Date(year, 8, 22), icon: '🍂' }, { name: 'Winter Solstice', date: new Date(year, 11, 21), icon: '❄️' }, { name: 'Year Start', date: new Date(year, 0, 1), icon: '🎆' }, { name: 'Mid-Year', date: new Date(year, 6, 1), icon: '📅' } ]; return dates.map(d => { const daysDiff = Math.floor((d.date - today) / (1000 * 60 * 60 * 24)); const isNear = Math.abs(daysDiff) <= 5; const isPast = daysDiff < 0; return { ...d, daysDiff, isNear, isPast, dateStr: d.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) }; }).sort((a, b) => a.date - b.date); } // Auto-fill Square of 9 with current price function autoFillSq9Price() { const input = document.getElementById('sq9Input'); if (input && currentData && currentData.length > 0) { const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; input.value = price.toFixed(2); updateSquareOf9(); } } // Update Square of 9 display function updateSquareOf9() { const input = document.getElementById('sq9Input'); const price = parseFloat(input?.value) || 0; if (price <= 0) return; const levels = calculateSquareOf9(price); if (!levels) return; // Update visual spiral const spiralEl = document.getElementById('sq9Spiral'); if (spiralEl) { spiralEl.innerHTML = `
    CENTER
    $${price.toFixed(2)}
    √${levels.sqrt.toFixed(4)}
    ${[45, 90, 135, 180, 225, 270, 315, 360].map((angle, i) => { const rad = (angle - 90) * Math.PI / 180; const r = 60 + (i * 5); const x = 90 + Math.cos(rad) * r; const y = 90 + Math.sin(rad) * r; return `
    ${angle}°
    `; }).join('')}
    `; } // Update support levels const supportEl = document.getElementById('sq9Support'); if (supportEl) { supportEl.innerHTML = levels.support.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} -${l.diffPct}%
    `).join(''); } // Update resistance levels const resistEl = document.getElementById('sq9Resistance'); if (resistEl) { resistEl.innerHTML = levels.resistance.map(l => `
    ${l.angle}° $${l.price.toFixed(2)} +${l.diffPct}%
    `).join(''); } return levels; } // Main Gann Analysis Update Function function updateGannAnalysis() { if (!currentData || currentData.length < 10) { console.warn('Insufficient data for Gann analysis'); return; } const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Update header var el=document.getElementById('gannCurrentSymbol');if(el)el.textContent = currentSymbol; var el=document.getElementById('gannCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); // Auto-fill and calculate Square of 9 const sq9Input = document.getElementById('sq9Input'); if (sq9Input && !sq9Input.value) { sq9Input.value = price.toFixed(2); } const sq9Levels = updateSquareOf9(); // Calculate Time Cycles const cycles = calculateGannCycles(); updateCycleDisplay(cycles); // Detect Pivots and Calculate Angles const pivots = detectGannPivots(currentData); updatePivotDisplay(pivots, price); // Calculate angles from pivots let anglesFromLow = []; let anglesFromHigh = []; if (pivots.low) { const daysSinceLow = pivots.dataLength - pivots.low.index; anglesFromLow = calculateGannAngles(pivots.low.price, daysSinceLow, price); } if (pivots.high) { const daysSinceHigh = pivots.dataLength - pivots.high.index; anglesFromHigh = calculateGannAngles(pivots.high.price, daysSinceHigh, price); } updateAnglesDisplay(anglesFromLow, anglesFromHigh, price); // Update Sacred Numbers updateSacredNumbers(price); // Calculate Confluence Score const confluence = calculateGannConfluence(sq9Levels, cycles, [...anglesFromLow, ...anglesFromHigh], price); updateConfluenceDisplay(confluence); // Generate Forecast generateGannForecast(sq9Levels, cycles, pivots, confluence, price); // Update Gann Dates updateGannDates(); // Rotate quote const quoteEl = document.getElementById('gannQuote'); if (quoteEl) { quoteEl.textContent = gannQuotes[Math.floor(Math.random() * gannQuotes.length)]; } // Update Gann Turn Analysis section updateGannTurnAnalysis(currentSymbol, price); console.log('Gann Analysis updated for', currentSymbol); } // Gann Timeframe Configuration const gannTimeframeConfig = { intraday: { label: '⚡ INTRADAY', displayName: '⚡ Intraday', cycleLength: '90 min cycle', dteRange: '0-1 DTE', color: '#f59e0b', bgColor: 'rgba(245,158,11,0.2)', // Intraday uses 90-minute cycles based on Gann time getNextTurn: function() { const now = new Date(); const marketOpen = new Date(now); marketOpen.setHours(9, 30, 0, 0); // 90-minute intervals from market open: 9:30, 11:00, 12:30, 2:00, 3:30 const turnTimes = [ { h: 11, m: 0 }, { h: 12, m: 30 }, { h: 14, m: 0 }, { h: 15, m: 30 } ]; for (const turn of turnTimes) { const turnTime = new Date(now); turnTime.setHours(turn.h, turn.m, 0, 0); if (turnTime > now) { return turnTime; } } // Next trading day 11:00 AM const nextDay = new Date(now); nextDay.setDate(nextDay.getDate() + 1); nextDay.setHours(11, 0, 0, 0); return nextDay; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, formatTimeUntil: function(date) { const now = new Date(); const diffMs = date - now; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const mins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) return `~${hours}h ${mins}m`; return `~${mins} min`; } }, swing: { label: '📊 SWING', displayName: '📊 Swing', cycleLength: '7-10 day cycle', dteRange: '3-14 DTE', color: '#3b82f6', bgColor: 'rgba(59,130,246,0.2)', // Swing uses key Gann dates (solstices, equinoxes, 45/90 day cycles) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Key Gann dates const gannDates = [ new Date(year, 11, 21), // Winter Solstice new Date(year, 2, 20), // Spring Equinox new Date(year, 5, 21), // Summer Solstice new Date(year, 8, 22), // Fall Equinox new Date(year + 1, 11, 21) ]; for (const date of gannDates) { if (date > now) return date; } return gannDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } }, position: { label: '🎯 POSITION', displayName: '🎯 Position', cycleLength: '30-90 day cycle', dteRange: '30+ DTE', color: '#22c55e', bgColor: 'rgba(34,197,94,0.2)', // Position uses major Gann cycle dates (quarters, annual) getNextTurn: function() { const now = new Date(); const year = now.getFullYear(); // Major quarterly dates const majorDates = [ new Date(year, 0, 20), // Jan new Date(year, 3, 20), // Apr new Date(year, 6, 20), // Jul new Date(year, 9, 20), // Oct new Date(year + 1, 0, 20) ]; for (const date of majorDates) { if (date > now) return date; } return majorDates[0]; }, formatTurn: function(date) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, formatTimeUntil: function(date) { const now = new Date(); const days = Math.ceil((date - now) / (1000 * 60 * 60 * 24)); return `${days} days`; } } }; let currentGannTimeframe = 'intraday'; // Update Gann Timeframe Selection function updateGannTimeframe(timeframe) { currentGannTimeframe = timeframe; const config = gannTimeframeConfig[timeframe]; // Update button styles ['intraday', 'swing', 'position'].forEach(tf => { const btn = document.getElementById('gannTf' + tf.charAt(0).toUpperCase() + tf.slice(1)); if (btn) { if (tf === timeframe) { btn.style.background = 'linear-gradient(135deg, #8b5cf6, #7c3aed)'; btn.style.color = 'white'; btn.style.border = 'none'; } else { btn.style.background = 'rgba(139,92,246,0.1)'; btn.style.color = '#8b5cf6'; btn.style.border = '1px solid rgba(139,92,246,0.3)'; } } }); // Update timeframe label const labelEl = document.getElementById('gannTimeframeLabel'); if (labelEl) { labelEl.textContent = config.label; labelEl.style.background = config.bgColor; labelEl.style.color = config.color; } // Update analysis date const analysisDateEl = document.getElementById('gannAnalysisDate'); if (analysisDateEl) { const now = new Date(); analysisDateEl.textContent = 'Analysis: ' + now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); } // Update 4-column grid const activeTimeframeEl = document.getElementById('gannActiveTimeframe'); const nextTurnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); const cycleLengthEl = document.getElementById('gannCycleLength'); const nextTurn = config.getNextTurn(); if (activeTimeframeEl) activeTimeframeEl.textContent = config.displayName; if (nextTurnDateEl) nextTurnDateEl.textContent = config.formatTurn(nextTurn); if (daysUntilEl) daysUntilEl.textContent = config.formatTimeUntil(nextTurn); if (cycleLengthEl) cycleLengthEl.textContent = config.cycleLength; // Get current price for calculations const symbol = currentSymbol || 'SPY'; var price = 100; var high = 105; var low = 95; if (symbolGannData && symbolGannData[symbol]) { price = parseFloat(symbolGannData[symbol].price) || 100; high = parseFloat(symbolGannData[symbol].high) || price * 1.05; low = parseFloat(symbolGannData[symbol].low) || price * 0.95; } else if (marketData && marketData[symbol]) { price = marketData[symbol].price || 100; high = price * 1.05; low = price * 0.95; } // Update prediction based on timeframe updateGannPrediction(timeframe, price, symbol, high, low); console.log('Gann timeframe changed to:', timeframe); } // Update Gann Prediction based on timeframe function updateGannPrediction(timeframe, price, symbol, high, low) { symbol = symbol || currentSymbol || 'SPY'; high = high || price * 1.05; low = low || price * 0.95; const config = gannTimeframeConfig[timeframe]; // Different prediction logic per timeframe // Intraday: Based on 90-min momentum // Swing: Based on weekly cycles // Position: Based on monthly/quarterly cycles const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Simulate different predictions per timeframe let isBullish = false; let reasons = []; let movePercent = { min: 0.5, max: 1.0 }; if (timeframe === 'intraday') { // Intraday - smaller moves, faster cycles isBullish = Math.random() > 0.5; // Would use real intraday data reasons = [ `90-min cycle: ${isBullish ? 'Rising' : 'Falling'} phase`, `Intraday VWAP: Price ${isBullish ? 'above' : 'below'} VWAP`, `Session high/low: Near session ${isBullish ? 'low (reversal zone)' : 'high (reversal zone)'}`, `Time window: Next 90 min turn approaching` ]; movePercent = { min: 0.3, max: 0.7 }; } else if (timeframe === 'swing') { // Swing - medium moves, weekly cycles isBullish = false; // Current trend is up, expect pullback reasons = [ `Current trend: UP (rallied from ${low.toFixed(2)} → ${price.toFixed(2)})`, `At Gann resistance: $${(price * 1.015).toFixed(2)} (90° level)`, `Cycle position: Day 22/30 (approaching cycle HIGH)`, `Time symmetry: Winter Solstice Dec 21 = major reversal` ]; movePercent = { min: 1.0, max: 2.5 }; } else { // Position - larger moves, monthly cycles isBullish = true; // Longer term outlook bullish reasons = [ `Major cycle: Q1 2026 bullish setup forming`, `Annual support: $${(price * 0.95).toFixed(2)} (360° level)`, `Seasonal pattern: Jan-Apr rally typical`, `Long-term Gann angle: 1x1 support holding` ]; movePercent = { min: 3.0, max: 8.0 }; } if (isBullish) { if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (turnTargetEl) { const low = (price * (1 + movePercent.min / 100)).toFixed(2); const high = (price * (1 + movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+' + movePercent.min + '% to +' + movePercent.max + '%)'; moveSizeEl.style.color = '#22c55e'; } } else { if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (turnTargetEl) { const high = (price * (1 - movePercent.min / 100)).toFixed(2); const low = (price * (1 - movePercent.max / 100)).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * movePercent.min / 100).toFixed(0); const maxMove = (price * movePercent.max / 100).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-' + movePercent.min + '% to -' + movePercent.max + '%)'; moveSizeEl.style.color = '#ef4444'; } } if (whyListEl) { whyListEl.innerHTML = reasons.map(r => '
  • ' + r + '
  • ').join(''); } } // Update Gann Turn Analysis Section function updateGannTurnAnalysis(symbol, price) { if (!price) { // Try to get price from marketData or currentData const data = marketData[symbol]; if (data) { price = data.price; } else if (currentData && currentData.length > 0) { price = parseFloat(currentData[currentData.length - 1].Close) || 607.09; } else { price = 607.09; // Default SPY price } } // Update symbol display const symbolEl = document.getElementById('gannTurnSymbol'); if (symbolEl) symbolEl.textContent = symbol || currentSymbol; // Determine trend direction based on recent price action const data = marketData[symbol] || {}; const isUptrend = (data.change >= 0) || (price > 600); // Default to uptrend for SPY above 600 // Calculate next turn date (Winter Solstice - Dec 21) const today = new Date(); const nextTurn = new Date(today.getFullYear(), 11, 21); // Dec 21 if (nextTurn < today) { nextTurn.setFullYear(nextTurn.getFullYear() + 1); } const daysUntil = Math.ceil((nextTurn - today) / (1000 * 60 * 60 * 24)); const turnDateEl = document.getElementById('gannNextTurnDate'); const daysUntilEl = document.getElementById('gannDaysUntil'); if (turnDateEl) { turnDateEl.textContent = nextTurn.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = daysUntil + ' days'; } // Get elements const predictionBox = document.getElementById('gannPredictionBox'); const predictedTurnEl = document.getElementById('gannPredictedTurn'); const tradeActionEl = document.getElementById('gannTradeAction'); const whyTitleEl = document.getElementById('gannWhyTitle'); const whyListEl = document.getElementById('gannWhyList'); const turnTargetEl = document.getElementById('gannTurnTarget'); const moveSizeEl = document.getElementById('gannMoveSize'); // Calculate Gann levels const resistance = (price * 1.015).toFixed(2); const support = (price * 0.985).toFixed(2); if (isUptrend) { // Uptrend = expect pullback if (predictionBox) { predictionBox.style.background = 'rgba(239,68,68,0.1)'; predictionBox.style.borderColor = '#ef4444'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔻 PULLBACK (TOP FORMING)'; predictedTurnEl.style.color = '#ef4444'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY PUT'; tradeActionEl.style.color = '#ef4444'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY PULLBACK?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: UP (recent bullish momentum)
  • At Gann resistance: $${resistance} (90° level)
  • Cycle position: Day 22/30 (approaching cycle HIGH)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 0.97).toFixed(2); const high = (price * 0.99).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#ef4444'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '-$' + minMove + ' to -$' + maxMove + ' (-1% to -3%)'; moveSizeEl.style.color = '#ef4444'; } } else { // Downtrend = expect rally if (predictionBox) { predictionBox.style.background = 'rgba(34,197,94,0.1)'; predictionBox.style.borderColor = '#22c55e'; } if (predictedTurnEl) { predictedTurnEl.textContent = '🔺 RALLY (BOTTOM FORMING)'; predictedTurnEl.style.color = '#22c55e'; } if (tradeActionEl) { tradeActionEl.textContent = 'BUY CALL'; tradeActionEl.style.color = '#22c55e'; } if (whyTitleEl) whyTitleEl.textContent = 'WHY RALLY?'; if (whyListEl) { whyListEl.innerHTML = `
  • Current trend: DOWN (recent bearish momentum)
  • At Gann support: $${support} (90° level)
  • Cycle position: Day 25/30 (approaching cycle LOW)
  • Time symmetry: Winter Solstice = major reversal window
  • `; } if (turnTargetEl) { const low = (price * 1.01).toFixed(2); const high = (price * 1.03).toFixed(2); turnTargetEl.textContent = '$' + low + ' - $' + high; turnTargetEl.style.color = '#22c55e'; } if (moveSizeEl) { const minMove = (price * 0.01).toFixed(0); const maxMove = (price * 0.03).toFixed(0); moveSizeEl.textContent = '+$' + minMove + ' to +$' + maxMove + ' (+1% to +3%)'; moveSizeEl.style.color = '#22c55e'; } } console.log('Gann Turn Analysis updated for', symbol, 'at $' + price.toFixed(2)); } // Update Cycle Display function updateCycleDisplay(cycles) { const progressEl = document.getElementById('cycleProgress'); const alertEl = document.getElementById('cycleAlert'); if (!progressEl) return; // Check for cycle turn zone const anyNearTurn = cycles.some(c => c.isNearTurn); if (alertEl) { alertEl.style.display = anyNearTurn ? 'block' : 'none'; } progressEl.innerHTML = cycles.map(cycle => { const fillColor = cycle.isNearTurn ? '#f59e0b' : cycle.color; return `
    ${cycle.name}
    ${cycle.daysUntilTurn}d
    `; }).join(''); } // Update Pivot Display function updatePivotDisplay(pivots, currentPrice) { const lowEl = document.getElementById('gannPivotLow'); const highEl = document.getElementById('gannPivotHigh'); if (lowEl && pivots.low) { const dateStr = new Date(pivots.low.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); lowEl.innerHTML = `$${pivots.low.price.toFixed(2)} (${dateStr})`; } if (highEl && pivots.high) { const dateStr = new Date(pivots.high.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); highEl.innerHTML = `$${pivots.high.price.toFixed(2)} (${dateStr})`; } } // Update Angles Display function updateAnglesDisplay(fromLow, fromHigh, currentPrice) { const lowEl = document.getElementById('anglesFromLow'); const highEl = document.getElementById('anglesFromHigh'); const positionEl = document.getElementById('anglePositionText'); if (lowEl) { lowEl.innerHTML = fromLow.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } if (highEl) { highEl.innerHTML = fromHigh.map(angle => `
    ${angle.name} (${angle.degrees}°)
    $${angle.price.toFixed(2)}
    `).join(''); } // Determine position if (positionEl) { const above1x1Low = fromLow.find(a => a.name === '1x1')?.isAbove; const below1x1High = fromHigh.find(a => a.name === '1x1')?.isAbove === false; let posText = ''; if (above1x1Low && below1x1High) { posText = 'Price BETWEEN 1x1 angles - Balanced zone'; } else if (above1x1Low) { posText = 'Price ABOVE 1x1 from low - Bullish momentum'; } else { posText = 'Price BELOW 1x1 from low - Bearish pressure'; } positionEl.textContent = posText; } } // Update Sacred Numbers Display function updateSacredNumbers(price) { const cardinalEl = document.getElementById('cardinalNumbers'); const sacredEl = document.getElementById('sacredNumbers'); const relEl = document.getElementById('priceRelationships'); if (cardinalEl) { const cardinals = [90, 180, 270, 360]; cardinalEl.innerHTML = cardinals.map(n => `
    ${n}°
    Cardinal
    `).join(''); } if (sacredEl) { const sacred = [ { val: 7, label: 'Days/Week' }, { val: 9, label: 'Square of 9' }, { val: 12, label: 'Months' }, { val: 52, label: 'Weeks/Yr' }, { val: 144, label: 'Fibonacci' } ]; sacredEl.innerHTML = sacred.map(s => `
    ${s.val}
    ${s.label}
    `).join(''); } if (relEl) { const sqrt = Math.sqrt(price); const squared = price * price; const div9 = price / 9; const times9 = price * 9; relEl.innerHTML = `
    √Price
    ${sqrt.toFixed(4)}
    Price²
    ${squared.toFixed(2)}
    Price ÷ 9
    ${div9.toFixed(4)}
    Price × 9
    ${times9.toFixed(2)}
    `; } } // Update Confluence Display function updateConfluenceDisplay(confluence) { const scoreEl = document.getElementById('confluenceScore'); const labelEl = document.getElementById('confluenceLabel'); const ringEl = document.getElementById('confluenceRing'); const factorsEl = document.getElementById('confluenceFactors'); if (scoreEl) { scoreEl.textContent = confluence.score; // Color based on score const color = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : confluence.score >= 25 ? '#f59e0b' : '#6b7280'; scoreEl.style.color = color; } if (labelEl) { const label = confluence.score >= 75 ? 'HIGH PROBABILITY ZONE' : confluence.score >= 50 ? 'MODERATE CONFLUENCE' : confluence.score >= 25 ? 'WEAK CONFLUENCE' : 'LOW PROBABILITY'; labelEl.textContent = label; labelEl.style.color = confluence.score >= 50 ? '#d4a017' : 'var(--muted)'; } if (ringEl) { const circumference = 314; // 2 * PI * 50 const offset = circumference - (confluence.score / 100 * circumference); ringEl.style.strokeDashoffset = offset; ringEl.style.stroke = confluence.score >= 75 ? '#22c55e' : confluence.score >= 50 ? '#d4a017' : '#6b7280'; } if (factorsEl) { factorsEl.innerHTML = confluence.factors.map(f => `
    ${f.active ? '✅' : '⬜'} ${f.text} ${f.active ? '+' + f.points : '0'}
    `).join(''); } } // Generate Gann Forecast function generateGannForecast(sq9Levels, cycles, pivots, confluence, price) { // Direction bias const biasEl = document.getElementById('gannBiasText'); const biasContainerEl = document.getElementById('gannBias'); let bias = 'NEUTRAL'; let biasColor = '#d4a017'; if (pivots.high && pivots.low) { const midPoint = (pivots.high.price + pivots.low.price) / 2; if (price > midPoint * 1.02) { bias = 'BULLISH'; biasColor = '#22c55e'; } else if (price < midPoint * 0.98) { bias = 'BEARISH'; biasColor = '#ef4444'; } } if (biasEl) { biasEl.textContent = bias; biasEl.style.color = biasColor; } // Support & Resistance from Square of 9 const supportEl = document.getElementById('forecastSupport'); const resistEl = document.getElementById('forecastResistance'); if (sq9Levels && supportEl && resistEl) { const supportVals = Object.values(sq9Levels.support || {}); const resistVals = Object.values(sq9Levels.resistance || {}); const nextSupport = supportVals[0]; // First support level const nextResist = resistVals[0]; // First resistance level supportEl.textContent = nextSupport ? '$' + (typeof nextSupport === 'object' ? nextSupport.price : nextSupport).toFixed(2) : '--'; resistEl.textContent = nextResist ? '$' + (typeof nextResist === 'object' ? nextResist.price : nextResist).toFixed(2) : '--'; } // Next cycle turn const cycleDateEl = document.getElementById('forecastCycleDate'); const daysUntilEl = document.getElementById('forecastDaysUntil'); if (cycles && cycles.length > 0) { // Find nearest cycle turn const sorted = [...cycles].sort((a, b) => a.daysUntilTurn - b.daysUntilTurn); const nearest = sorted[0]; if (cycleDateEl) { cycleDateEl.textContent = nearest.nextTurnDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } if (daysUntilEl) { daysUntilEl.textContent = nearest.daysUntilTurn + ' days'; daysUntilEl.style.color = nearest.daysUntilTurn <= 3 ? '#f59e0b' : '#8b5cf6'; } } // Projected move const projectedEl = document.getElementById('projectedMoveText'); if (projectedEl && sq9Levels) { const atr = parseFloat(currentData[currentData.length - 1]?.ATR) || price * 0.015; const projectedMove = atr * 2; const nearestCycleDays = cycles ? Math.min(...cycles.map(c => c.daysUntilTurn)) : 30; const direction = bias === 'BULLISH' ? '↑' : bias === 'BEARISH' ? '↓' : '↔'; projectedEl.textContent = `Expect ${direction} $${projectedMove.toFixed(2)} move within ${nearestCycleDays} days`; } } // Update Gann Dates function updateGannDates() { const datesEl = document.getElementById('gannDates'); if (!datesEl) return; const dates = getGannDates(); datesEl.innerHTML = dates.map(d => `
    ${d.icon} ${d.name} ${d.dateStr}
    `).join(''); } // ============================================ // OVERVIEW TAB UPDATE FUNCTIONS // ============================================ // Update Fibonacci Zone Display function updateFibonacciZoneDisplay() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate 52-week high/low for Fibonacci levels const lookback = Math.min(252, currentData.length); const yearData = currentData.slice(-lookback); let high52 = 0, low52 = Infinity; yearData.forEach(bar => { const h = parseFloat(bar.High) || 0; const l = parseFloat(bar.Low) || Infinity; if (h > high52) high52 = h; if (l < low52) low52 = l; }); const range = high52 - low52; const s1 = low52 + range * 0.382; const s2 = low52 + range * 0.5; const s3 = low52 + range * 0.618; const r1 = high52; // 100% const r2 = high52 + range * 0.272; const r3 = high52 + range * 0.618; // Update prices var el=document.getElementById('fibS3');if(el)el.textContent = '$' + s3.toFixed(2); var el=document.getElementById('fibS2');if(el)el.textContent = '$' + s2.toFixed(2); var el=document.getElementById('fibS1');if(el)el.textContent = '$' + s1.toFixed(2); var el=document.getElementById('fibCurrentPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('fibR1');if(el)el.textContent = '$' + r1.toFixed(2); var el=document.getElementById('fibR2');if(el)el.textContent = '$' + r2.toFixed(2); var el=document.getElementById('fibR3');if(el)el.textContent = '$' + r3.toFixed(2); // Determine zone let zoneLabel = ''; let zoneColor = '#22c55e'; if (price < s3) { zoneLabel = '🔴 Below S3 - Oversold Zone'; zoneColor = '#ef4444'; } else if (price < s2) { zoneLabel = '🟡 Between S3 and S2 - Support Zone'; zoneColor = '#eab308'; } else if (price < s1) { zoneLabel = '🟢 Between S2 and S1 - Recovery Zone'; zoneColor = '#22c55e'; } else if (price < r1) { zoneLabel = '🟢 Between S1 and R1 - Neutral Zone'; zoneColor = '#22c55e'; } else if (price < r2) { zoneLabel = '🟡 Between R1 and R2 - Resistance Zone'; zoneColor = '#eab308'; } else if (price < r3) { zoneLabel = '🟠 Between R2 and R3 - Extended Zone'; zoneColor = '#f97316'; } else { zoneLabel = '🔴 Above R3 - Overbought Zone'; zoneColor = '#ef4444'; } const zoneLabelEl = document.getElementById('fibZoneLabel'); if (zoneLabelEl) { zoneLabelEl.textContent = zoneLabel; zoneLabelEl.style.color = zoneColor; } } // Update Multi-Timeframe Trends function updateMultiTimeframeTrends() { if (!currentData || currentData.length < 20) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; // Calculate SMAs for different timeframes const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma10 = currentData.slice(-10).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 10; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; // Intraday trend (price vs 5 SMA) const intradayTrend = price > sma5 ? 'BULLISH' : 'BEARISH'; const intradayColor = price > sma5 ? '#22c55e' : '#ef4444'; // Swing trend (price vs 10 SMA and 5 vs 10) const swingBullish = price > sma10 && sma5 > sma10; const swingTrend = swingBullish ? 'BULLISH' : 'BEARISH'; const swingColor = swingBullish ? '#22c55e' : '#ef4444'; // Position trend (price vs 20 SMA) const positionTrend = price > sma20 ? 'BULLISH' : 'BEARISH'; const positionColor = price > sma20 ? '#22c55e' : '#ef4444'; var el=document.getElementById('trendIntraday');if(el)el.textContent = intradayTrend; document.getElementById('trendIntraday').style.color = intradayColor; var el=document.getElementById('trendSwing');if(el)el.textContent = swingTrend; document.getElementById('trendSwing').style.color = swingColor; var el=document.getElementById('trendPosition');if(el)el.textContent = positionTrend; document.getElementById('trendPosition').style.color = positionColor; } // Update Gann Quick View function updateGannQuickView() { if (!currentData || currentData.length < 10) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const bias = lastBar.Bias || 'NEUTRAL'; // Trend const trendEl = document.getElementById('gannQvTrend'); if (trendEl) { trendEl.textContent = bias; trendEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Volume vs Average const avgVol = currentData.slice(-20).reduce((sum, d) => sum + (parseInt(d.Volume) || 0), 0) / 20; const todayVol = parseInt(lastBar.Volume) || 0; const volRatio = avgVol > 0 ? ((todayVol / avgVol) * 100).toFixed(0) : 100; const volEl = document.getElementById('gannQvVolume'); if (volEl) { volEl.textContent = volRatio + '%'; volEl.style.color = parseInt(volRatio) > 120 ? '#22c55e' : parseInt(volRatio) < 80 ? '#ef4444' : 'var(--text)'; } // Bars in current trend let barsInTrend = 0; const currentDirection = parseFloat(currentData[currentData.length - 1].Close) > parseFloat(currentData[currentData.length - 2].Close) ? 'up' : 'down'; for (let i = currentData.length - 1; i > 0; i--) { const prevDir = parseFloat(currentData[i].Close) > parseFloat(currentData[i - 1].Close) ? 'up' : 'down'; if (prevDir === currentDirection) { barsInTrend++; } else { break; } } const barsEl = document.getElementById('gannQvBars'); if (barsEl) { barsEl.textContent = barsInTrend + ' bars'; barsEl.style.color = barsInTrend > 5 ? '#f59e0b' : 'var(--text)'; } // Cycle Phase const dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycleDay = dayOfYear % 30; const cyclePhaseEl = document.getElementById('gannQvCyclePhase'); if (cyclePhaseEl) { cyclePhaseEl.textContent = 'Day ' + cycleDay + ' of 30'; } // Next Cycle Turn const daysToTurn = 30 - cycleDay; const nextTurnEl = document.getElementById('gannQvNextTurn'); if (nextTurnEl) { const nextDate = new Date(); nextDate.setDate(nextDate.getDate() + daysToTurn); nextTurnEl.textContent = nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); nextTurnEl.style.color = daysToTurn <= 3 ? '#f59e0b' : 'var(--text)'; } // Gann Score const scoreEl = document.getElementById('gannQvScore'); if (scoreEl) { let score = 50; if (bias === 'BULLISH') score += 15; if (parseInt(volRatio) > 100) score += 10; if (barsInTrend >= 3) score += 10; score = Math.min(100, score); scoreEl.textContent = score + '/100'; scoreEl.style.color = score >= 75 ? '#22c55e' : score >= 50 ? '#d4a017' : '#ef4444'; } // Agent Consensus const consensusEl = document.getElementById('gannQvConsensus'); if (consensusEl) { consensusEl.textContent = bias === 'BULLISH' ? 'BUY' : bias === 'BEARISH' ? 'SELL' : 'HOLD'; consensusEl.style.color = bias === 'BULLISH' ? '#22c55e' : bias === 'BEARISH' ? '#ef4444' : '#d4a017'; } // Reversal Risk const reversalEl = document.getElementById('gannQvReversal'); if (reversalEl) { const risk = barsInTrend > 7 ? 'HIGH' : barsInTrend > 4 ? 'MEDIUM' : 'LOW'; reversalEl.textContent = risk; reversalEl.style.color = risk === 'HIGH' ? '#ef4444' : risk === 'MEDIUM' ? '#f59e0b' : '#22c55e'; } // MTF Alignment const mtfEl = document.getElementById('gannQvMtf'); if (mtfEl) { const sma5 = currentData.slice(-5).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 5; const sma20 = currentData.slice(-20).reduce((sum, d) => sum + parseFloat(d.Close || 0), 0) / 20; const aligned = (price > sma5 && sma5 > sma20) || (price < sma5 && sma5 < sma20); mtfEl.textContent = aligned ? 'ALIGNED' : 'MIXED'; mtfEl.style.color = aligned ? '#22c55e' : '#f59e0b'; } // Entry Window const entryEl = document.getElementById('gannQvEntry'); if (entryEl) { const entryScore = (barsInTrend < 5 && parseInt(volRatio) > 90) ? 'OPEN' : 'WAIT'; entryEl.textContent = entryScore; entryEl.style.color = entryScore === 'OPEN' ? '#22c55e' : '#f59e0b'; } } // Symbol to full name mapping const symbolNames = { 'SPY': 'S&P 500', 'QQQ': 'Nasdaq 100', 'IWM': 'Russell 2000', 'DIA': 'Dow Jones', 'XLK': 'Technology', 'XLF': 'Financials', 'XLE': 'Energy', 'XLV': 'Healthcare', 'XLI': 'Industrials', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLB': 'Materials', 'XLC': 'Communications', 'XLRE': 'Real Estate', 'XLY': 'Consumer Disc', 'VXX': 'Volatility', 'TLT': 'Treasury Bonds', 'GLD': 'Gold', 'SLV': 'Silver', 'USO': 'Oil', 'UNG': 'Natural Gas', 'EEM': 'Emerging Mkts', 'EFA': 'Intl Developed', 'HYG': 'High Yield', 'LQD': 'Investment Grade', 'ARKK': 'Innovation', 'SMH': 'Semiconductors', 'XBI': 'Biotech', 'KRE': 'Regional Banks', 'XHB': 'Homebuilders' }; // Update Trade Statistics function updateTradeStatistics() { if (!currentData || currentData.length < 30) return; const last30 = currentData.slice(-30); let wins = 0, losses = 0; let totalWin = 0, totalLoss = 0; let bestDay = 0, worstDay = 0; let streak = 0, currentStreakDir = null; for (let i = 1; i < last30.length; i++) { const change = parseFloat(last30[i].Close) - parseFloat(last30[i - 1].Close); const changeDollar = change; if (change > 0) { wins++; totalWin += changeDollar; if (changeDollar > bestDay) bestDay = changeDollar; if (currentStreakDir === 'win') streak++; else { streak = 1; currentStreakDir = 'win'; } } else if (change < 0) { losses++; totalLoss += Math.abs(changeDollar); if (changeDollar < worstDay) worstDay = changeDollar; if (currentStreakDir === 'loss') streak++; else { streak = 1; currentStreakDir = 'loss'; } } } const totalTrades = wins + losses; const winRate = totalTrades > 0 ? ((wins / totalTrades) * 100).toFixed(0) : 0; const avgWin = wins > 0 ? (totalWin / wins).toFixed(2) : 0; const avgLoss = losses > 0 ? (totalLoss / losses).toFixed(2) : 0; // Update dynamic title with symbol name const symbolName = symbolNames[currentSymbol] || currentSymbol; const titleEl = document.getElementById('statsTitle'); if (titleEl) { titleEl.textContent = `${currentSymbol} - ${symbolName} 📊 Trade Statistics (Last 30 Days)`; } var el=document.getElementById('statsWinRate');if(el)el.textContent = winRate + '%'; var el=document.getElementById('statsAvgWin');if(el)el.textContent = '$' + avgWin; var el=document.getElementById('statsAvgLoss');if(el)el.textContent = '$' + avgLoss; var el=document.getElementById('statsTotalTrades');if(el)el.textContent = totalTrades; var el=document.getElementById('statsBestDay');if(el)el.textContent = '+$' + bestDay.toFixed(2); var el=document.getElementById('statsWorstDay');if(el)el.textContent = '$' + worstDay.toFixed(2); var el=document.getElementById('statsStreak');if(el)el.textContent = streak + ' ' + (currentStreakDir === 'win' ? '🟢' : '🔴'); } // Market Events Calendar Data const marketEvents = { '2025-12-06': '👷 Jobs Report', '2025-12-11': '📈 CPI', '2025-12-17': '🏛️ FOMC Decision', '2025-12-18': '🏛️ FOMC', '2025-12-20': '📅 OpEx (Monthly)', '2025-12-24': '🎄 Early Close', '2025-12-25': '🎄 Market Closed', '2025-12-31': '🎆 NYE Early Close', '2026-01-01': '🎆 Market Closed', '2026-01-03': '👷 Jobs Report', '2026-01-10': '📈 CPI', '2026-01-15': '📅 OpEx (Monthly)', '2026-01-20': '🏛️ MLK Day Closed', '2026-01-29': '🏛️ FOMC', '2026-02-07': '👷 Jobs Report', '2026-02-12': '📈 CPI', '2026-02-21': '📅 OpEx (Monthly)', '2026-03-07': '👷 Jobs Report', '2026-03-12': '📈 CPI', '2026-03-18': '🏛️ FOMC', '2026-03-21': '📅 OpEx (Monthly)' }; // Detect candle type function detectCandleType(open, high, low, close) { const body = Math.abs(close - open); const range = high - low; const upperWick = high - Math.max(open, close); const lowerWick = Math.min(open, close) - low; if (range === 0) return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; const bodyRatio = body / range; const upperWickRatio = upperWick / range; const lowerWickRatio = lowerWick / range; // Doji - small body, long wicks if (bodyRatio < 0.1) { return { type: 'Doji', icon: '⚡', color: '#f59e0b' }; } // Hammer - small body at top, long lower wick (bullish reversal) if (lowerWickRatio > 0.6 && upperWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Hammer', icon: '🔨', color: '#22c55e' }; } // Shooting Star - small body at bottom, long upper wick (bearish reversal) if (upperWickRatio > 0.6 && lowerWickRatio < 0.1 && bodyRatio < 0.3) { return { type: 'Star', icon: '⭐', color: '#ef4444' }; } // Strong candles - big body, small wicks if (bodyRatio > 0.7) { if (close > open) { return { type: 'Strong Bull', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Strong Bear', icon: '🔴', color: '#ef4444' }; } } // Regular candles if (close > open) { return { type: 'Bullish', icon: '🟢', color: '#22c55e' }; } else { return { type: 'Bearish', icon: '🔴', color: '#ef4444' }; } } // Update Upcoming Events Widget function updateUpcomingEventsWidget() { const widget = document.getElementById('upcomingEventsWidget'); if (!widget) return; const today = new Date(); const next7Days = []; for (let i = 0; i < 7; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'short' }); const dateDisplay = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); if (marketEvents[dateStr]) { next7Days.push({ date: dateDisplay, day: dayName, event: marketEvents[dateStr], daysAway: i }); } } if (next7Days.length === 0) { widget.innerHTML = '
    No major events in next 7 days
    '; return; } widget.innerHTML = next7Days.map(e => `
    ${e.day} ${e.date}
    ${e.event}
    ${e.daysAway === 0 ? '⚠️ TODAY' : e.daysAway === 1 ? 'Tomorrow' : `In ${e.daysAway} days`}
    `).join(''); } // Update Pattern Detection Summary - Now uses table format function updatePatternDetectionSummary() { // Redirect to table-based pattern detection if (typeof updatePatternTableFromCSV === 'function') { updatePatternTableFromCSV(); } } // Update Historical Data Table with all new columns function updateHistoricalDataTable() { if (!currentData || currentData.length < 10) return; const tbody = document.getElementById('historicalDataTable'); const summaryEl = document.getElementById('historicalSummary'); if (!tbody) return; // Calculate 20-day average volume const avgVol20 = currentData.slice(-20).reduce((s, d) => s + (parseInt(d.Volume) || 0), 0) / 20; const last10 = currentData.slice(-10).reverse(); let html = ''; // Summary counters let gapUps = 0, gapDowns = 0, hhhlCount = 0, lhllCount = 0, highVolDays = 0, lowVolDays = 0; let nextEventDays = null, nextEventName = ''; // Find next event const today = new Date(); for (let i = 0; i < 14; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() + i); const dateStr = checkDate.toISOString().split('T')[0]; if (marketEvents[dateStr]) { nextEventDays = i; nextEventName = marketEvents[dateStr]; break; } } last10.forEach((bar, index) => { const date = bar.Date || bar.date || '--'; const dateKey = new Date(date).toISOString().split('T')[0]; const open = parseFloat(bar.Open) || 0; const high = parseFloat(bar.High) || 0; const low = parseFloat(bar.Low) || 0; const close = parseFloat(bar.Close) || 0; const volume = parseInt(bar.Volume) || 0; // Get previous bar data (remember: array is reversed) const prevBar = last10[index + 1]; const prevClose = prevBar ? parseFloat(prevBar.Close) || close : close; const prevHigh = prevBar ? parseFloat(prevBar.High) || high : high; const prevLow = prevBar ? parseFloat(prevBar.Low) || low : low; // Change calculation const change = close - prevClose; const changePct = prevClose > 0 ? ((change / prevClose) * 100).toFixed(2) : 0; // GAP calculation const gap = open - prevClose; const gapPct = prevClose > 0 ? ((gap / prevClose) * 100).toFixed(2) : 0; let gapDisplay = '', gapColor = ''; if (Math.abs(parseFloat(gapPct)) >= 0.5) { if (gap > 0) { gapDisplay = `🟢 +$${gap.toFixed(2)}`; gapColor = '#22c55e'; gapUps++; } else { gapDisplay = `🔴 $${gap.toFixed(2)}`; gapColor = '#ef4444'; gapDowns++; } } else { gapDisplay = `➡️ $${gap.toFixed(2)}`; gapColor = 'var(--muted)'; } // Candle Type const candle = detectCandleType(open, high, low, close); // Trend Signal (HH/HL or LH/LL) let trendSignal = '', trendColor = ''; if (prevBar) { const higherHigh = high > prevHigh; const higherLow = low > prevLow; const lowerHigh = high < prevHigh; const lowerLow = low < prevLow; if (higherHigh && higherLow) { trendSignal = '📈 HH/HL'; trendColor = '#22c55e'; hhhlCount++; } else if (lowerHigh && lowerLow) { trendSignal = '📉 LH/LL'; trendColor = '#ef4444'; lhllCount++; } else { trendSignal = '↔️ Mixed'; trendColor = '#f59e0b'; } } else { trendSignal = '--'; trendColor = 'var(--muted)'; } // Close Position (where did price close in day's range) const range = high - low; const closePos = range > 0 ? ((close - low) / range * 100).toFixed(0) : 50; let closePosDisplay = '', closePosColor = ''; if (parseInt(closePos) >= 75) { closePosDisplay = '🟢 Upper'; closePosColor = '#22c55e'; } else if (parseInt(closePos) <= 25) { closePosDisplay = '🔴 Lower'; closePosColor = '#ef4444'; } else { closePosDisplay = '➡️ Mid'; closePosColor = 'var(--muted)'; } // Volume Signal const volRatio = avgVol20 > 0 ? ((volume / avgVol20) * 100).toFixed(0) : 100; let volDisplay = '', volColor = ''; if (parseInt(volRatio) >= 150) { volDisplay = `🔺 +${volRatio - 100}%`; volColor = '#22c55e'; highVolDays++; } else if (parseInt(volRatio) <= 70) { volDisplay = `🔻 ${volRatio - 100}%`; volColor = '#ef4444'; lowVolDays++; } else { volDisplay = '➡️ Norm'; volColor = 'var(--muted)'; } // O→H Signal const openToLow = open - low; const openToHigh = high - open; const ohSignal = openToLow < openToHigh ? '🟢' : '🔴'; const ohColor = openToLow < openToHigh ? '#22c55e' : '#ef4444'; // Events const event = marketEvents[dateKey] || '-'; // Row background color let rowBg = ''; if (event !== '-') rowBg = 'background: rgba(245,158,11,0.1);'; else if (parseInt(volRatio) >= 150) rowBg = 'background: rgba(59,130,246,0.1);'; else if (candle.type === 'Hammer' || candle.type === 'Star') rowBg = 'background: rgba(249,115,22,0.1);'; const dateFormatted = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const changeColor = change >= 0 ? '#22c55e' : '#ef4444'; const changeSign = change >= 0 ? '+' : ''; html += ` ${dateFormatted} $${open.toFixed(0)}/${high.toFixed(0)}/${low.toFixed(0)}/$${close.toFixed(2)} ${changeSign}${changePct}% ${gapDisplay} ${candle.icon} ${candle.type} ${trendSignal} ${closePosDisplay} ${volDisplay} ${ohSignal} ${event} `; }); tbody.innerHTML = html; // Update summary row if (summaryEl) { const trendDesc = hhhlCount > lhllCount + 2 ? '📈 Uptrend Confirmed' : lhllCount > hhhlCount + 2 ? '📉 Downtrend Confirmed' : '↔️ Consolidation'; const volDesc = highVolDays > 3 ? '🔺 High Volume' : lowVolDays > 5 ? '🔻 Low Volume (⚠️ Weak)' : '➡️ Normal Volume'; let nextEventHtml = ''; if (nextEventDays !== null) { nextEventHtml = `
    Next Event: ${nextEventName} in ${nextEventDays} day${nextEventDays !== 1 ? 's' : ''}
    `; } summaryEl.innerHTML = `
    Gaps: ${gapUps} Up, ${gapDowns} Down
    Trend: ${hhhlCount} HH/HL, ${lhllCount} LH/LL → ${trendDesc}
    Volume: ${volDesc}
    ${nextEventHtml} `; } // Also update related widgets updateUpcomingEventsWidget(); updatePatternDetectionSummary(); } // Update Overview Reference Data function updateOverviewReferenceData() { if (!currentData || currentData.length < 2) return; const lastBar = currentData[currentData.length - 1]; const prevBar = currentData[currentData.length - 2]; // Today Open const openEl = document.getElementById('todayOpen'); if (openEl) openEl.textContent = '$' + (parseFloat(lastBar.Open) || 0).toFixed(2); // Prev Close const prevCloseEl = document.getElementById('prevClose'); if (prevCloseEl) prevCloseEl.textContent = '$' + (parseFloat(prevBar.Close) || 0).toFixed(2); } // Master Overview Update Function function updateOverviewTab() { updateFibonacciZoneDisplay(); updateMultiTimeframeTrends(); updateGannQuickView(); updateTradeStatistics(); updateHistoricalDataTable(); updateOverviewReferenceData(); } // Load Overview data directly from CSV async function loadOverviewData() { const symbol = document.querySelector('#symbolSelect')?.value || 'SPY'; try { // Try data folder first, then root let response = await fetch('data/' + symbol + '.csv').catch(() => null); if (!response?.ok) { console.log('Could not load ' + symbol + '.csv'); return; } const text = await response.text(); const lines = text.trim().split('\n'); if (lines.length < 2) return; // Parse header to get column indices const header = lines[0].split(','); const getIndex = (name) => header.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); // Get last row (most recent data) const lastLine = lines[lines.length - 1]; const cols = lastLine.split(','); // Get previous row for change calculation const prevLine = lines.length > 2 ? lines[lines.length - 2] : lastLine; const prevCols = prevLine.split(','); // Extract values using header or fallback positions const close = parseFloat(cols[getIndex('close')] || cols[4]) || 0; const open = parseFloat(cols[getIndex('open')] || cols[1]) || 0; const high = parseFloat(cols[getIndex('high')] || cols[2]) || 0; const low = parseFloat(cols[getIndex('low')] || cols[3]) || 0; const volume = parseFloat(cols[getIndex('volume')] || cols[5]) || 0; const atr = parseFloat(cols[getIndex('atr')] || cols[6]) || 0; const prevClose = parseFloat(prevCols[getIndex('close')] || prevCols[4]) || close; // Calculate change const change = close - prevClose; const changePct = prevClose ? ((change / prevClose) * 100) : 0; // Update Price metric const priceEl = document.getElementById('currentPrice'); if (priceEl) priceEl.textContent = '$' + close.toFixed(2); // Update Change const changeEl = document.getElementById('dailyChange'); if (changeEl) { changeEl.textContent = (change >= 0 ? '+' : '') + '$' + Math.abs(change).toFixed(2); changeEl.className = 'metric-value ' + (change >= 0 ? 'positive' : 'negative'); } const changePctEl = document.getElementById('changePct'); if (changePctEl) changePctEl.textContent = (changePct >= 0 ? '+' : '') + changePct.toFixed(2) + '%'; // Update Volume const volEl = document.getElementById('volumeDisplay'); if (volEl) volEl.textContent = (volume / 1000000).toFixed(1) + 'M'; // Update ATR const atrEl = document.getElementById('atrDisplay'); if (atrEl) atrEl.textContent = '$' + atr.toFixed(2); // Calculate Fibonacci levels based on recent high/low let recentHigh = high; let recentLow = low; // Get high/low from last 20 bars for (let i = Math.max(1, lines.length - 20); i < lines.length; i++) { const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > recentHigh) recentHigh = h; if (l < recentLow || recentLow === 0) recentLow = l; } const range = recentHigh - recentLow; // Fibonacci levels const fib382 = recentHigh - (range * 0.382); const fib500 = recentHigh - (range * 0.500); const fib618 = recentHigh - (range * 0.618); const fib100 = recentHigh; const fib1272 = recentLow + (range * 1.272); const fib1618 = recentLow + (range * 1.618); // Update Fibonacci Price Zones const fibS1 = document.getElementById('fibS1'); const fibS2 = document.getElementById('fibS2'); const fibS3 = document.getElementById('fibS3'); const fibR1 = document.getElementById('fibR1'); const fibR2 = document.getElementById('fibR2'); const fibR3 = document.getElementById('fibR3'); const fibCurrentPrice = document.getElementById('fibCurrentPrice'); if (fibS1) fibS1.textContent = '$' + fib382.toFixed(2); if (fibS2) fibS2.textContent = '$' + fib500.toFixed(2); if (fibS3) fibS3.textContent = '$' + fib618.toFixed(2); if (fibR1) fibR1.textContent = '$' + fib100.toFixed(2); if (fibR2) fibR2.textContent = '$' + fib1272.toFixed(2); if (fibR3) fibR3.textContent = '$' + fib1618.toFixed(2); if (fibCurrentPrice) fibCurrentPrice.textContent = '$' + close.toFixed(2); // Calculate 52-week high/low from all data (up to 252 bars) let high52w = 0; let low52w = Infinity; const barsFor52w = Math.min(lines.length - 1, 252); for (let i = lines.length - barsFor52w; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); const h = parseFloat(rowCols[getIndex('high')] || rowCols[2]) || 0; const l = parseFloat(rowCols[getIndex('low')] || rowCols[3]) || 0; if (h > high52w) high52w = h; if (l < low52w && l > 0) low52w = l; } if (low52w === Infinity) low52w = low; // Update Reference Data const week52HighEl = document.getElementById('week52High'); const week52LowEl = document.getElementById('week52Low'); const todayHighEl = document.getElementById('todayHigh'); const todayLowEl = document.getElementById('todayLow'); const todayOpenEl = document.getElementById('todayOpen'); const prevCloseEl = document.getElementById('prevClose'); if (week52HighEl) week52HighEl.textContent = '$' + high52w.toFixed(2); if (week52LowEl) week52LowEl.textContent = '$' + low52w.toFixed(2); if (todayHighEl) todayHighEl.textContent = '$' + high.toFixed(2); if (todayLowEl) todayLowEl.textContent = '$' + low.toFixed(2); if (todayOpenEl) todayOpenEl.textContent = '$' + open.toFixed(2); if (prevCloseEl) prevCloseEl.textContent = '$' + prevClose.toFixed(2); // Calculate average volume (last 20 bars) let totalVolume = 0; const volBars = Math.min(lines.length - 1, 20); for (let i = lines.length - volBars; i < lines.length; i++) { if (i < 1) continue; const rowCols = lines[i].split(','); totalVolume += parseFloat(rowCols[getIndex('volume')] || rowCols[5]) || 0; } const avgVol = totalVolume / volBars; const avgVolEl = document.getElementById('avgVolume20'); const volVsAvgEl = document.getElementById('volumeVsAvg'); const volVsAvgSmallEl = document.getElementById('volumeVsAvgSmall'); if (avgVolEl) avgVolEl.textContent = (avgVol / 1e6).toFixed(1) + 'M'; if (volVsAvgEl) volVsAvgEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '%'; if (volVsAvgSmallEl) volVsAvgSmallEl.textContent = ((volume / avgVol - 1) * 100).toFixed(0) + '% vs avg'; console.log('Overview data loaded for ' + symbol); } catch (error) { console.log('Error loading overview data:', error); } } function generateDailyReport() { console.log('Generating daily report...', currentAnalysisMode); if (!currentData || currentData.length === 0) { console.warn('No data available for analysis'); return; } // Get current symbol data const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close) || 0; const prevClose = parseFloat(currentData[currentData.length - 2]?.Close) || price; // Calculate all signal components const signals = calculateAllSignals(currentData, price, prevClose); // Update the UI updateConfluenceScore(signals); updateTradeRecommendations(signals); updateGannAnalysis(price); updateFibonacciLevels(currentData); updateHighConsensusTrades(signals); updateMarketContext(signals); updateAISummary(signals, price); updatePlanDateTime(); // Store for download dailyAnalysisData = { mode: currentAnalysisMode, timestamp: new Date().toISOString(), symbol: currentSymbol, price: price, signals: signals }; // Update analysis timestamp updateSectionTimestamp('analysis'); } function calculateAllSignals(data, price, prevClose) { const signals = { agentConsensus: { score: 0, detail: '', count: 0, agentDetails: null, isReal: false }, barCount: { score: 0, detail: '', count: 0 }, patterns: { score: 0, detail: '', found: [] }, gann: { score: 0, detail: '' }, fibonacci: { score: 0, detail: '', level: '' }, volume: { score: 0, detail: '' }, masterScore: 0, direction: 'NEUTRAL', confidence: 50 }; // 1. Agent Consensus Score (with individual agent breakdown) const agentSignals = getAgentSignals(); signals.agentConsensus.count = agentSignals.agreeing; signals.agentConsensus.score = (agentSignals.agreeing / 4) * 4; // Max 4 points signals.agentConsensus.signal = agentSignals.signal; signals.agentConsensus.isReal = agentSignals.isReal; signals.agentConsensus.agentDetails = agentSignals.agentDetails; signals.agentConsensus.timestamp = agentSignals.timestamp; signals.agentConsensus.detail = `${agentSignals.agreeing}/4 agents: ${agentSignals.signal}${agentSignals.isReal ? ' (LIVE)' : ' (calc)'}`; // 2. Bar Count Score const barCount = calculateBarCountSignal(data); signals.barCount.count = barCount.count; signals.barCount.score = Math.min(Math.abs(barCount.count) / 5, 2) * (barCount.count > 0 ? 1 : -1); // Max ±2 signals.barCount.detail = `${barCount.count > 0 ? '+' : ''}${barCount.count} consecutive ${barCount.direction} bars`; // 3. Pattern Score const patterns = detectAllPatterns(data); signals.patterns.found = patterns.found; signals.patterns.score = patterns.score; // Max ±3 signals.patterns.detail = patterns.found.length > 0 ? patterns.found.join(', ') : 'No patterns detected'; // 4. Gann Cycle Score const gann = calculateGannCycleScore(data, price); signals.gann.score = gann.score; // Max ±2 signals.gann.detail = gann.detail; // 5. Fibonacci Score const fib = calculateFibonacciScore(data, price); signals.fibonacci.score = fib.score; // Max ±2 signals.fibonacci.detail = fib.detail; signals.fibonacci.level = fib.level; // 6. Volume Score const volume = calculateVolumeScore(data); signals.volume.score = volume.score; // Max ±1.5 signals.volume.detail = volume.detail; // Calculate Master Score signals.masterScore = ( signals.agentConsensus.score + signals.barCount.score + signals.patterns.score + signals.gann.score + signals.fibonacci.score + signals.volume.score ); // Determine direction and confidence if (signals.masterScore >= 3) { signals.direction = 'BULLISH'; signals.confidence = Math.min(50 + signals.masterScore * 5, 95); } else if (signals.masterScore <= -3) { signals.direction = 'BEARISH'; signals.confidence = Math.min(50 + Math.abs(signals.masterScore) * 5, 95); } else { signals.direction = 'NEUTRAL'; signals.confidence = 50 + Math.abs(signals.masterScore) * 3; } return signals; } // Real agent voting data (loaded from voting_log.json) let realAgentVotes = null; async function loadRealAgentSignals() { try { const response = await fetch('voting_log.json'); if (response.ok) { const data = await response.json(); realAgentVotes = data; console.log('✅ Loaded real agent signals from voting_log.json'); return true; } } catch (e) { console.log('⚠️ voting_log.json not available, using calculated signals'); } return false; } function getAgentSignals() { // Try to get REAL signals from voting_log.json first if (realAgentVotes && realAgentVotes.votes && realAgentVotes.votes.length > 0) { const latestVote = realAgentVotes.votes[realAgentVotes.votes.length - 1]; // Parse individual agent signals const agentDetails = { base: { signal: 'HOLD', confidence: 0 }, gann: { signal: 'HOLD', confidence: 0 }, dqn: { signal: 'HOLD', confidence: 0 }, wave: { signal: 'HOLD', confidence: 0 } }; let callCount = 0, putCount = 0, holdCount = 0; if (latestVote.component_signals) { latestVote.component_signals.forEach(sig => { const agentName = sig.agent?.toLowerCase() || ''; const signal = sig.signal?.toUpperCase() || 'HOLD'; const confidence = sig.confidence || 0; if (agentName.includes('base') || agentName.includes('confluence')) { agentDetails.base = { signal, confidence }; } else if (agentName.includes('gann') || agentName.includes('elliott')) { agentDetails.gann = { signal, confidence }; } else if (agentName.includes('dqn') || agentName.includes('ml')) { agentDetails.dqn = { signal, confidence }; } else if (agentName.includes('wave') || agentName.includes('3')) { agentDetails.wave = { signal, confidence }; } if (signal === 'CALL') callCount++; else if (signal === 'PUT') putCount++; else holdCount++; }); } const finalSignal = latestVote.final_signal?.toUpperCase() || 'HOLD'; const agreementLevel = latestVote.agreement_level || Math.max(callCount, putCount, holdCount); return { signal: finalSignal, agreeing: agreementLevel, callCount, putCount, holdCount, isReal: true, agentDetails, timestamp: latestVote.timestamp }; } // FALLBACK: Calculate signals from price data if voting_log.json not available let callCount = 0, putCount = 0, holdCount = 0; const agentDetails = { base: { signal: 'HOLD', confidence: 50 }, gann: { signal: 'HOLD', confidence: 50 }, dqn: { signal: 'HOLD', confidence: 50 }, wave: { signal: 'HOLD', confidence: 50 } }; const lastBars = currentData.slice(-5); const trend = lastBars[lastBars.length - 1]?.Close > lastBars[0]?.Close; const momentum = calculateMomentum(currentData); // Base Confluence Agent if (trend && momentum > 0) { callCount++; agentDetails.base = { signal: 'CALL', confidence: 60 + momentum * 20 }; } else if (!trend && momentum < 0) { putCount++; agentDetails.base = { signal: 'PUT', confidence: 60 + Math.abs(momentum) * 20 }; } else { holdCount++; agentDetails.base = { signal: 'HOLD', confidence: 50 }; } // Gann-Elliott Agent if (trend) { callCount++; agentDetails.gann = { signal: 'CALL', confidence: 65 }; } else { putCount++; agentDetails.gann = { signal: 'PUT', confidence: 65 }; } // DQN Agent if (momentum > 0.5) { callCount++; agentDetails.dqn = { signal: 'CALL', confidence: 70 + momentum * 15 }; } else if (momentum < -0.5) { putCount++; agentDetails.dqn = { signal: 'PUT', confidence: 70 + Math.abs(momentum) * 15 }; } else { holdCount++; agentDetails.dqn = { signal: 'HOLD', confidence: 50 }; } // 3-Wave Agent if (trend) { callCount++; agentDetails.wave = { signal: 'CALL', confidence: 60 }; } else { putCount++; agentDetails.wave = { signal: 'PUT', confidence: 60 }; } const maxCount = Math.max(callCount, putCount, holdCount); let signal = 'HOLD'; let agreeing = holdCount; if (callCount === maxCount && callCount > putCount) { signal = 'CALL'; agreeing = callCount; } else if (putCount === maxCount) { signal = 'PUT'; agreeing = putCount; } return { signal, agreeing, callCount, putCount, holdCount, isReal: false, agentDetails, timestamp: null }; } function calculateMomentum(data) { if (data.length < 14) return 0; const recent = data.slice(-14); const gains = [], losses = []; for (let i = 1; i < recent.length; i++) { const change = parseFloat(recent[i].Close) - parseFloat(recent[i-1].Close); if (change > 0) gains.push(change); else losses.push(Math.abs(change)); } const avgGain = gains.length > 0 ? gains.reduce((a,b) => a+b, 0) / 14 : 0; const avgLoss = losses.length > 0 ? losses.reduce((a,b) => a+b, 0) / 14 : 0; if (avgLoss === 0) return 1; const rs = avgGain / avgLoss; const rsi = 100 - (100 / (1 + rs)); return (rsi - 50) / 50; // Normalize to -1 to 1 } function calculateBarCountSignal(data) { if (data.length < 2) return { count: 0, direction: 'neutral' }; let count = 0; let direction = 'neutral'; // Count consecutive bars in same direction for (let i = data.length - 1; i > 0; i--) { const curr = parseFloat(data[i].Close); const prev = parseFloat(data[i-1].Close); if (curr > prev) { if (direction === 'neutral' || direction === 'bullish') { direction = 'bullish'; count++; } else break; } else if (curr < prev) { if (direction === 'neutral' || direction === 'bearish') { direction = 'bearish'; count--; } else break; } else break; } return { count, direction }; } function detectAllPatterns(data) { const found = []; let score = 0; if (data.length < 20) return { found, score }; const recent = data.slice(-20); const closes = recent.map(d => parseFloat(d.Close)); const highs = recent.map(d => parseFloat(d.High)); const lows = recent.map(d => parseFloat(d.Low)); // Bull Flag const highestHigh = Math.max(...highs.slice(0, 10)); const recentHigh = Math.max(...highs.slice(-5)); const recentLow = Math.min(...lows.slice(-5)); if (recentHigh < highestHigh * 0.98 && recentLow > highestHigh * 0.95) { found.push('Bull Flag'); score += 1.5; } // Bear Flag const lowestLow = Math.min(...lows.slice(0, 10)); if (recentLow > lowestLow * 1.02 && recentHigh < lowestLow * 1.05) { found.push('Bear Flag'); score -= 1.5; } // Golden Cross (9 SMA > 21 SMA) const sma9 = closes.slice(-9).reduce((a,b) => a+b, 0) / 9; const sma21 = closes.slice(-21).reduce((a,b) => a+b, 0) / 21; if (sma9 > sma21) { found.push('Golden Cross'); score += 1; } else { found.push('Death Cross'); score -= 1; } // Higher Highs / Lower Lows const recentHighs = highs.slice(-5); const recentLows = lows.slice(-5); let higherHighs = 0, lowerLows = 0; for (let i = 1; i < recentHighs.length; i++) { if (recentHighs[i] > recentHighs[i-1]) higherHighs++; if (recentLows[i] < recentLows[i-1]) lowerLows++; } if (higherHighs >= 3) { found.push('Higher Highs'); score += 0.5; } if (lowerLows >= 3) { found.push('Lower Lows'); score -= 0.5; } return { found, score: Math.max(-3, Math.min(3, score)) }; } function calculateGannCycleScore(data, price) { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Gann believed in natural cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; let score = 0; let details = []; // 30-day cycle if (cycle30 < 7) { details.push('30-day: Bottoming'); score += 0.5; } else if (cycle30 > 23) { details.push('30-day: Topping'); score -= 0.5; } else { details.push('30-day: Mid-cycle'); } // 90-day cycle if (cycle90 < 20) { details.push('90-day: Accumulation'); score += 0.5; } else if (cycle90 > 70) { details.push('90-day: Distribution'); score -= 0.5; } else { details.push('90-day: Trend phase'); score += 0.25; } // Seasonal (Q4 typically bullish) const month = today.getMonth(); if (month >= 9) { // Oct-Dec details.push('Seasonal: Q4 bullish'); score += 0.5; } else if (month >= 4 && month <= 6) { // May-Jul details.push('Seasonal: Summer lull'); score -= 0.25; } return { score: Math.max(-2, Math.min(2, score)), detail: details.join('; ') }; } function calculateFibonacciScore(data, price) { if (data.length < 50) return { score: 0, detail: 'Insufficient data', level: '' }; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; // Calculate Fibonacci levels const levels = { '23.6%': high - range * 0.236, '38.2%': high - range * 0.382, '50%': high - range * 0.5, '61.8%': high - range * 0.618, '78.6%': high - range * 0.786 }; // Find nearest level let nearestLevel = ''; let nearestDist = Infinity; for (const [name, levelPrice] of Object.entries(levels)) { const dist = Math.abs(price - levelPrice); if (dist < nearestDist) { nearestDist = dist; nearestLevel = name; } } let score = 0; let detail = ''; // Score based on position relative to Fibonacci levels const pricePosition = (high - price) / range; if (pricePosition < 0.236) { score = 1.5; // Near highs - bullish continuation detail = 'Above 23.6% - Strong bullish'; } else if (pricePosition < 0.382) { score = 1; // Shallow pullback detail = 'At 23.6-38.2% - Bullish pullback'; } else if (pricePosition < 0.5) { score = 0.5; // Normal retracement detail = 'At 38.2-50% - Moderate support'; } else if (pricePosition < 0.618) { score = 0; // Neutral detail = 'At 50-61.8% - Critical zone'; } else if (pricePosition < 0.786) { score = -0.5; // Deep retracement detail = 'At 61.8-78.6% - Deep pullback'; } else { score = -1.5; // Near lows - bearish detail = 'Below 78.6% - Bearish'; } return { score, detail, level: nearestLevel }; } function calculateVolumeScore(data) { if (data.length < 20) return { score: 0, detail: 'Insufficient data' }; const volumes = data.slice(-20).map(d => parseFloat(d.Volume) || 0); const avgVolume = volumes.slice(0, -1).reduce((a,b) => a+b, 0) / 19; const todayVolume = volumes[volumes.length - 1]; const volumeRatio = todayVolume / avgVolume; let score = 0; let detail = ''; if (volumeRatio > 1.5) { score = 1.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - High conviction`; } else if (volumeRatio > 1.2) { score = 1; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Above average`; } else if (volumeRatio > 0.8) { score = 0; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Normal`; } else { score = -0.5; detail = `Volume ${(volumeRatio * 100).toFixed(0)}% of avg - Low interest`; } return { score, detail }; } function updateConfluenceScore(signals) { const scoreEl = document.getElementById('masterConfluenceScore'); const directionEl = document.getElementById('confluenceDirection'); const confidenceEl = document.getElementById('confluenceConfidence'); const symbolEl = document.getElementById('confluenceSymbol'); if (scoreEl) { scoreEl.textContent = (signals.masterScore > 0 ? '+' : '') + signals.masterScore.toFixed(1); scoreEl.className = 'confluence-score ' + (signals.masterScore > 0 ? 'bullish' : signals.masterScore < 0 ? 'bearish' : 'neutral'); } if (directionEl) { directionEl.textContent = signals.direction + ' BIAS'; directionEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (confidenceEl) confidenceEl.textContent = signals.confidence.toFixed(0) + '%'; if (symbolEl) symbolEl.textContent = currentSymbol + ' Analysis'; // Update individual signal scores updateSignalItem('agentScore', signals.agentConsensus.count + '/4', signals.agentConsensus.score > 0); updateSignalItem('agentDetail', signals.agentConsensus.detail); updateSignalItem('barCountScore', (signals.barCount.count > 0 ? '+' : '') + signals.barCount.count, signals.barCount.score > 0); updateSignalItem('barCountDetail', signals.barCount.detail); updateSignalItem('patternScore', (signals.patterns.score > 0 ? '+' : '') + signals.patterns.score.toFixed(1), signals.patterns.score > 0); updateSignalItem('patternDetail', signals.patterns.detail); updateSignalItem('gannScore', (signals.gann.score > 0 ? '+' : '') + signals.gann.score.toFixed(1), signals.gann.score > 0); updateSignalItem('gannDetail', signals.gann.detail); updateSignalItem('fibScore', (signals.fibonacci.score > 0 ? '+' : '') + signals.fibonacci.score.toFixed(1), signals.fibonacci.score > 0); updateSignalItem('fibDetail', signals.fibonacci.detail); updateSignalItem('volumeScore', (signals.volume.score > 0 ? '+' : '') + signals.volume.score.toFixed(1), signals.volume.score > 0); updateSignalItem('volumeDetail', signals.volume.detail); } function updateSignalItem(id, value, isBullish) { const el = document.getElementById(id); if (!el) return; if (id.includes('Score')) { el.textContent = value; el.className = 'signal-item-score ' + (isBullish ? 'bullish' : isBullish === false ? 'bearish' : 'neutral'); } else { el.textContent = value; } } function updateTradeRecommendations(signals) { const container = document.getElementById('tradeRecommendations'); if (!container) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; // Calculate all price levels const entryLow = price - atr * 0.3; const entryHigh = price + atr * 0.1; const bestEntry = signal === 'CALL' ? entryLow : entryHigh; const stop05 = signal === 'CALL' ? price - atr * 0.5 : price + atr * 0.5; const stop10 = signal === 'CALL' ? price - atr * 1.0 : price + atr * 1.0; const target1 = signal === 'CALL' ? price + atr * 1.5 : price - atr * 1.5; const target2 = signal === 'CALL' ? price + atr * 2.5 : price - atr * 2.5; // Calculate optimal strike const strike = Math.round(price / 5) * 5; // Round to nearest $5 // DTE explanation based on mode const dteValue = tradingMode === 'INTRADAY' ? '0-1' : '3-7'; const dteExplain = tradingMode === 'INTRADAY' ? 'Expires today/tomorrow. High risk, high reward. Quick profits but rapid time decay.' : 'Expires in 3-7 days. Gives trade time to develop. Less time decay pressure.'; container.innerHTML = `
    ${currentSymbol} ${signal}
    🎯 Best Entry Price: $${bestEntry.toFixed(2)}
    Wait for pullback to this level for optimal risk/reward
    Entry Range: $${entryLow.toFixed(2)} - $${entryHigh.toFixed(2)}
    ⚠️ Stop Loss Options:
    • Tight (0.5 ATR): $${stop05.toFixed(2)} (=$${(atr * 0.5).toFixed(2)} risk)
    • Standard (1.0 ATR): $${stop10.toFixed(2)} (=$${atr.toFixed(2)} risk)
    🎯 Profit Targets:
    • Target 1: $${target1.toFixed(2)} (take 50%)
    • Target 2: $${target2.toFixed(2)} (close position)
    📋 Options Setup:
    • Strike: $${strike} ${signal}
    • DTE: ${dteValue} DTE (Days To Expiration)
    💡 ${dteExplain}

    📊 Why This Signal? ${signals.agentConsensus.isReal ? 'LIVE DATA' : 'CALCULATED'}

    🤖 Individual Agent Votes:
    ${signals.agentConsensus.agentDetails ? `
    📊 Base Confluence: ${signals.agentConsensus.agentDetails.base.signal} (${signals.agentConsensus.agentDetails.base.confidence.toFixed(0)}%)
    🔮 Gann-Elliott: ${signals.agentConsensus.agentDetails.gann.signal} (${signals.agentConsensus.agentDetails.gann.confidence.toFixed(0)}%)
    🧠 DQN (ML): ${signals.agentConsensus.agentDetails.dqn.signal} (${signals.agentConsensus.agentDetails.dqn.confidence.toFixed(0)}%)
    🌊 3-Wave: ${signals.agentConsensus.agentDetails.wave.signal} (${signals.agentConsensus.agentDetails.wave.confidence.toFixed(0)}%)
    ` : '
    Agent data not available
    '}
    Consensus: ${signals.agentConsensus.count}/4 agents → ${signals.agentConsensus.signal}
    🔢 Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} bars
    📐 Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.slice(0,2).join(', ') : 'None'}
    🔮 Gann Cycle: ${signals.gann.score > 0 ? '🟢 Bullish' : signals.gann.score < 0 ? '🔴 Bearish' : '🟡 Neutral'}
    📐 Fibonacci: ${signals.fibonacci.level}
    📊 Volume: ${signals.volume.score > 0 ? '🟢 Above Avg' : '🔴 Below Avg'}
    Overall Confidence
    ${signals.confidence.toFixed(0)}%
    📖 Understanding ATR (Average True Range):
    Current ATR = $${atr.toFixed(2)} — This means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    0.5 ATR stop = $${(atr * 0.5).toFixed(2)} risk — Tighter stop, less room for normal fluctuation
    1.0 ATR stop = $${atr.toFixed(2)} risk — Standard stop, accounts for normal daily volatility
    • Use tighter stops for day trades, wider stops for swing trades
    `; } function updateGannAnalysis(price) { const timeCyclesEl = document.getElementById('gannTimeCycles'); const sq9El = document.getElementById('squareOf9'); const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); // Time cycles const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const cycle180 = dayOfYear % 180; // Get cycle phases with explanations let cycle30Phase, cycle30Action; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Action = '🟢 Look for reversal longs'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Action = '🔴 Caution, take profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Action = '🟢 Favor bullish plays'; } else { cycle30Phase = 'Declining'; cycle30Action = '🟡 Patience, wait for bottom'; } let cycle90Phase, cycle90Action; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Action = '🟢 Smart money buying'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Action = '🔴 Smart money selling'; } else { cycle90Phase = 'Trend Phase'; cycle90Action = '🟢 Ride the momentum'; } if (timeCyclesEl) { timeCyclesEl.innerHTML = `
    30-day cycle: Day ${cycle30}/30 - ${cycle30Phase}
    ${cycle30Action} | ${cycle30Phase === 'Bottoming' ? 'Market finding floor, reversal expected' : cycle30Phase === 'Topping' ? 'Market at peak, reversal risk high' : cycle30Phase === 'Rising' ? 'Upward momentum active' : 'Downward pressure, wait for cycle bottom'}
    90-day cycle: Day ${cycle90}/90 - ${cycle90Phase}
    ${cycle90Action} | ${cycle90Phase === 'Accumulation' ? 'Institutional buying phase' : cycle90Phase === 'Distribution' ? 'Institutional selling phase' : 'Strong trending, follow direction'}
    Next Gann Date: ${getNextGannDate()}
    💡 Gann dates (equinoxes/solstices) often mark trend reversals
    `; } // Square of 9 calculations with explanations if (sq9El) { const sq9Levels = calculateSquareOf9(price); sq9El.innerHTML = `
    Resistance Levels:
    • R1: $${sq9Levels.r1.toFixed(2)} (45° up)
    • R2: $${sq9Levels.r2.toFixed(2)} (90° up)
    Support Levels:
    • S1: $${sq9Levels.s1.toFixed(2)} (45° down)
    • S2: $${sq9Levels.s2.toFixed(2)} (90° down)
    💡 What is Square of 9?
    Gann's geometric calculator that finds natural price levels. Each 45° rotation = 1 step. Prices tend to find support/resistance at these harmonic levels.
    `; } } function getNextGannDate() { const today = new Date(); const gannDates = [ { month: 0, day: 21 }, // Jan 21 { month: 2, day: 21 }, // Mar 21 (Equinox) { month: 5, day: 21 }, // Jun 21 (Solstice) { month: 8, day: 21 }, // Sep 21 (Equinox) { month: 11, day: 21 }, // Dec 21 (Solstice) ]; for (const gd of gannDates) { const date = new Date(today.getFullYear(), gd.month, gd.day); if (date > today) { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } } return new Date(today.getFullYear() + 1, 0, 21).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function calculateSquareOf9(price) { // Gann Square of 9 calculations const sqrt = Math.sqrt(price); const increment = 0.125; // 45 degrees = 0.125 return { r1: Math.pow(sqrt + increment, 2), r2: Math.pow(sqrt + increment * 2, 2), s1: Math.pow(sqrt - increment, 2), s2: Math.pow(sqrt - increment * 2, 2) }; } function updateFibonacciLevels(data) { if (data.length < 50) return; const recent = data.slice(-50); const high = Math.max(...recent.map(d => parseFloat(d.High))); const low = Math.min(...recent.map(d => parseFloat(d.Low))); const range = high - low; const levels = { 'phi236': high - range * 0.236, 'phi382': high - range * 0.382, 'phi50': high - range * 0.5, 'phi618': high - range * 0.618, 'phi786': high - range * 0.786, 'phi1618': high + range * 0.618 }; for (const [id, value] of Object.entries(levels)) { const el = document.getElementById(id); if (el) el.textContent = '$' + value.toFixed(2); } } function updateHighConsensusTrades(signals) { const tableBody = document.getElementById('highConsensusTable'); if (!tableBody) return; const lastBar = currentData[currentData.length - 1]; const price = parseFloat(lastBar.Close); const atr = parseFloat(lastBar.ATR) || price * 0.015; const signal = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'; const consensus = signals.agentConsensus.count; if (consensus >= 3) { const stop = signal === 'CALL' ? price - atr * 1.5 : price + atr * 1.5; const target = signal === 'CALL' ? price + atr * 2 : price - atr * 2; const strike = Math.round(price / 5) * 5; tableBody.innerHTML = ` ${currentSymbol} ${signal} ${consensus}/4 ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} $${(price - atr * 0.3).toFixed(2)} - $${(price + atr * 0.3).toFixed(2)} $${stop.toFixed(2)} $${target.toFixed(2)} $${strike} ${signal} `; } else { tableBody.innerHTML = 'No high-consensus trades at this time'; } } function updateMarketContext(signals) { const biasEl = document.getElementById('overallBias'); const leadersEl = document.getElementById('sectorLeaders'); const riskEl = document.getElementById('riskLevel'); if (biasEl) { biasEl.textContent = signals.direction; biasEl.style.color = signals.direction === 'BULLISH' ? 'var(--success)' : signals.direction === 'BEARISH' ? 'var(--danger)' : 'var(--muted)'; } if (leadersEl) { leadersEl.textContent = signals.direction === 'BULLISH' ? 'XLK, QQQ' : signals.direction === 'BEARISH' ? 'XLU, XLP' : 'Mixed'; } if (riskEl) { const risk = signals.confidence > 75 ? 'LOW' : signals.confidence > 60 ? 'MODERATE' : 'HIGH'; riskEl.textContent = risk; riskEl.style.color = risk === 'LOW' ? 'var(--success)' : risk === 'MODERATE' ? 'var(--warning)' : 'var(--danger)'; } } function updateAISummary(signals, price) { const summaryEl = document.getElementById('aiSummary'); if (!summaryEl) return; const lastBar = currentData[currentData.length - 1]; const atr = parseFloat(lastBar.ATR) || price * 0.015; const sq9 = calculateSquareOf9(price); const signalWord = signals.direction === 'BULLISH' ? 'bullish' : signals.direction === 'BEARISH' ? 'bearish' : 'neutral'; const optionType = signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'neutral'; // Calculate actual price levels const entryLow = (price - atr * 0.3).toFixed(2); const entryHigh = (price + atr * 0.1).toFixed(2); const bestEntry = signals.direction === 'BULLISH' ? entryLow : entryHigh; const stopLoss05ATR = signals.direction === 'BULLISH' ? (price - atr * 0.5).toFixed(2) : (price + atr * 0.5).toFixed(2); const stopLoss10ATR = signals.direction === 'BULLISH' ? (price - atr * 1.0).toFixed(2) : (price + atr * 1.0).toFixed(2); const target1 = signals.direction === 'BULLISH' ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); const target2 = signals.direction === 'BULLISH' ? (price + atr * 2.5).toFixed(2) : (price - atr * 2.5).toFixed(2); // Get Gann cycle explanations const gannExplanations = getGannCycleExplanations(); // Calculate optimal strike const optimalStrike = Math.round(price / 5) * 5; if (currentAnalysisMode === 'morning') { summaryEl.innerHTML = ` ☀️ Morning Analysis (${new Date().toLocaleDateString()}): Market conditions favor ${signalWord} positions today. ${currentSymbol} showing ${signals.agentConsensus.count}/4 agent consensus for ${signals.agentConsensus.signal}. Master confluence score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} with ${signals.confidence.toFixed(0)}% confidence.

    🎯 Recommended Entry Zone:
    Best Entry Price: $${bestEntry}
    Entry Range: $${entryLow} - $${entryHigh}
    Why this range? Based on ATR (Average True Range = $${atr.toFixed(2)}), this zone offers optimal risk/reward
    📊 Recommended Strategy:
    ${signals.confidence >= 70 ? `
    Trade: ${optionType} spreads on ${currentSymbol}
    Strike: $${optimalStrike} ${optionType}
    DTE: ${tradingMode === 'INTRADAY' ? '0-1 DTE' : '3-7 DTE'}
    💡 DTE = Days To Expiration. ${tradingMode === 'INTRADAY' ? '0-1 DTE means options expiring today or tomorrow - high risk/reward for quick moves.' : '3-7 DTE means 3-7 days until expiration - gives time for the move to develop with less theta decay.'}
    ` : `
    ⚠️ Wait for higher confidence setup. Current confluence is weak.
    Consider smaller position sizes or sitting out until signals align better.
    `} 🔮 Gann Cycle Analysis:
    ${gannExplanations}
    ⚠️ Stop Loss Levels (with explanations):
    Tight Stop (0.5 ATR): $${stopLoss05ATR}
    💡 0.5 × $${atr.toFixed(2)} ATR = $${(atr * 0.5).toFixed(2)} from entry → Stop at $${stopLoss05ATR}
    Use for: Day trades, high confidence setups, smaller position sizes

    Standard Stop (1.0 ATR): $${stopLoss10ATR}
    💡 1.0 × $${atr.toFixed(2)} ATR = $${atr.toFixed(2)} from entry → Stop at $${stopLoss10ATR}
    Use for: Swing trades, giving room for normal volatility
    🎯 Profit Targets:
    Target 1 (1.5 ATR): $${target1} - Take 50% profits
    Target 2 (2.5 ATR): $${target2} - Take remaining position
    Resistance (Sq9): $${sq9.r1.toFixed(2)} - Major resistance from Square of 9
    📖 Key Terms Explained:
    ATR (Average True Range): Measures volatility. Current ATR = $${atr.toFixed(2)} means ${currentSymbol} typically moves $${atr.toFixed(2)} per day.
    DTE (Days To Expiration): How many days until your option expires. Lower DTE = higher risk but bigger potential gains.
    Sq9 (Square of 9): Gann's calculator for finding natural support/resistance levels based on price geometry.
    Confluence: Multiple signals agreeing on the same direction = higher probability trade.
    `; } else { summaryEl.innerHTML = ` 🌙 Evening Review (${new Date().toLocaleDateString()}): Today's session ended with ${currentSymbol} at $${price.toFixed(2)}. ${signals.direction === 'BULLISH' ? 'Bulls maintained control' : signals.direction === 'BEARISH' ? 'Bears dominated' : 'Mixed price action'} with ${signals.agentConsensus.count}/4 agent agreement.

    📋 Day Summary:
    • Master Score: ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)} (${signals.direction})
    • Patterns: ${signals.patterns.found.length > 0 ? signals.patterns.found.join(', ') : 'None detected'}
    • Bar Count: ${signals.barCount.count > 0 ? '+' : ''}${signals.barCount.count} consecutive ${signals.barCount.count > 0 ? 'bullish' : 'bearish'} bars
    • Volume: ${signals.volume.detail}
    📅 Tomorrow's Trading Plan:
    ${signals.confidence >= 65 ? ` Bias: Continue ${signalWord} outlook
    Watch for: Gap ${signals.direction === 'BULLISH' ? 'up' : 'down'} at open
    Entry Zone: $${entryLow} - $${entryHigh}
    Best Entry: $${bestEntry} (wait for pullback to this level)
    Stop Loss: $${stopLoss10ATR} (1 ATR = $${atr.toFixed(2)} below entry)
    Target: $${target1} (first target), $${sq9.r1.toFixed(2)} (Sq9 resistance) ` : ` ⚠️ Low confidence setup for tomorrow.
    Wait for clearer signals before committing capital.
    Key levels to watch: Support $${sq9.s1.toFixed(2)} / Resistance $${sq9.r1.toFixed(2)} `}
    🔮 Gann Cycle Outlook:
    ${gannExplanations}
    🎯 Pre-Market Checklist for Tomorrow:
    ☐ Check overnight futures direction (ES, NQ)
    ☐ Review any earnings/economic news
    ☐ Confirm agent consensus at 9:35 AM
    ☐ Size positions based on confidence (${signals.confidence.toFixed(0)}%)
    ☐ Set alerts at $${sq9.s1.toFixed(2)} (support) and $${sq9.r1.toFixed(2)} (resistance)
    `; } } function getGannCycleExplanations() { const today = new Date(); const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); const cycle30 = dayOfYear % 30; const cycle90 = dayOfYear % 90; const month = today.getMonth(); let explanations = ''; // 30-day cycle explanation let cycle30Phase, cycle30Explain; if (cycle30 < 7) { cycle30Phase = 'Bottoming'; cycle30Explain = 'Market finding a floor - look for reversal signals to go long'; } else if (cycle30 > 23) { cycle30Phase = 'Topping'; cycle30Explain = 'Market reaching peak - be cautious of reversals, consider taking profits'; } else if (cycle30 < 15) { cycle30Phase = 'Rising'; cycle30Explain = 'Upward momentum phase - favor bullish positions'; } else { cycle30Phase = 'Declining'; cycle30Explain = 'Downward momentum phase - favor bearish positions or wait'; } // 90-day cycle explanation let cycle90Phase, cycle90Explain; if (cycle90 < 20) { cycle90Phase = 'Accumulation'; cycle90Explain = 'Smart money buying - early bullish opportunity'; } else if (cycle90 > 70) { cycle90Phase = 'Distribution'; cycle90Explain = 'Smart money selling - late in cycle, increased risk'; } else { cycle90Phase = 'Trend Phase'; cycle90Explain = 'Strong trending period - follow the momentum'; } // Seasonal explanation let seasonal, seasonalExplain; if (month >= 9) { // Oct-Dec seasonal = 'Q4 Bullish'; seasonalExplain = 'Historically strongest quarter - Santa Rally, year-end positioning'; } else if (month >= 4 && month <= 6) { seasonal = 'Summer Lull'; seasonalExplain = '"Sell in May" period - typically lower volatility and returns'; } else if (month >= 0 && month <= 2) { seasonal = 'January Effect'; seasonalExplain = 'New year inflows, tax-loss harvesting reversals'; } else { seasonal = 'Transitional'; seasonalExplain = 'Between major seasonal patterns'; } explanations = ` • 30-Day Cycle: ${cycle30Phase} (Day ${cycle30}/30)
    💡 ${cycle30Explain}

    90-Day Cycle: ${cycle90Phase} (Day ${cycle90}/90)
    💡 ${cycle90Explain}

    Seasonal: ${seasonal}
    💡 ${seasonalExplain} `; return explanations; } function updatePlanDateTime() { const now = new Date(); const dateEl = document.getElementById('planDate'); const timeEl = document.getElementById('planTime'); if (dateEl) { dateEl.textContent = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } if (timeEl) { timeEl.textContent = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + ' ET'; } } function downloadReport() { if (!dailyAnalysisData) { alert('Please generate a report first'); return; } const content = generateReportHTML(); const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Q5D_${currentSymbol}_${currentAnalysisMode}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function generateReportHTML() { const signals = dailyAnalysisData.signals; const price = dailyAnalysisData.price; const sq9 = calculateSquareOf9(price); return ` Q5D Trading Report - ${currentSymbol} - ${new Date().toLocaleDateString()}

    📊 Q5D Trading Report

    ${currentAnalysisMode === 'morning' ? '☀️ Morning Plan' : '🌙 Evening Review'}

    ${currentSymbol}

    ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

    ${new Date().toLocaleTimeString()}

    ${signals.masterScore > 0 ? '+' : ''}${signals.masterScore.toFixed(1)}

    ${signals.direction} BIAS | Confidence: ${signals.confidence.toFixed(0)}%

    📊 Signal Breakdown

    Signal ComponentScoreDetails
    🤖 Agent Consensus${signals.agentConsensus.count}/4${signals.agentConsensus.detail}
    🔢 Bar Count${signals.barCount.score > 0 ? '+' : ''}${signals.barCount.score.toFixed(1)}${signals.barCount.detail}
    📐 Patterns${signals.patterns.score > 0 ? '+' : ''}${signals.patterns.score.toFixed(1)}${signals.patterns.detail}
    🔮 Gann Cycles${signals.gann.score > 0 ? '+' : ''}${signals.gann.score.toFixed(1)}${signals.gann.detail}
    📐 Fibonacci${signals.fibonacci.score > 0 ? '+' : ''}${signals.fibonacci.score.toFixed(1)}${signals.fibonacci.detail}
    📊 Volume${signals.volume.score > 0 ? '+' : ''}${signals.volume.score.toFixed(1)}${signals.volume.detail}

    🎯 Trade Recommendation

    Symbol: ${currentSymbol} | Current Price: $${price.toFixed(2)}

    Signal: ${signals.direction === 'BULLISH' ? 'CALL' : signals.direction === 'BEARISH' ? 'PUT' : 'HOLD'}

    Optimal Strike: $${Math.round(price / 5) * 5}

    Stop Loss: $${sq9.s1.toFixed(2)}

    Target: $${sq9.r1.toFixed(2)}

    🔮 Gann Square of 9 Levels

    Resistance

    R1: $${sq9.r1.toFixed(2)}

    R2: $${sq9.r2.toFixed(2)}

    Support

    S1: $${sq9.s1.toFixed(2)}

    S2: $${sq9.s2.toFixed(2)}

    `; } // ========== OPTIONS CALCULATOR ========== // Default IV values for each ETF const ETF_DEFAULT_IV = { SPY: 18, QQQ: 22, IWM: 25, DIA: 16, XLK: 24, XLF: 20, XLV: 18, XLE: 30, XLI: 19 }; let currentOptionType = 'call'; function setOptionType(type) { currentOptionType = type; document.querySelectorAll('.option-type-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.option-type-btn.' + type).classList.add('active'); calculateOptions(); } function useCurrentSymbolPrice() { const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); document.getElementById('calcSymbol').value = currentSymbol; const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } else { alert('Price data not available yet. Please wait for data to load.'); } } function updateOptionsCalcForSymbol() { document.getElementById('calcSymbol').value = currentSymbol; const priceEl = document.getElementById('currentPrice'); const priceText = priceEl ? priceEl.textContent : ''; const price = parseFloat(priceText.replace(/[$,]/g, '')); if (price && price > 0) { document.getElementById('calcStockPrice').value = price.toFixed(2); const strikeRound = price > 100 ? 5 : 1; document.getElementById('calcStrikePrice').value = Math.round(price / strikeRound) * strikeRound; } const iv = ETF_DEFAULT_IV[currentSymbol] || 20; document.getElementById('calcIV').value = iv; calculateOptions(); } function normalCDF(x) { const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741; const a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911; const sign = x < 0 ? -1 : 1; x = Math.abs(x) / Math.sqrt(2); const t = 1.0 / (1.0 + p * x); const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); return 0.5 * (1.0 + sign * y); } function normalPDF(x) { return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function calculateOptions() { const S = parseFloat(document.getElementById('calcStockPrice').value) || 0; const K = parseFloat(document.getElementById('calcStrikePrice').value) || 0; const T = (parseFloat(document.getElementById('calcDTE').value) || 1) / 365; const r = (parseFloat(document.getElementById('calcRiskFree').value) || 5) / 100; const sigma = (parseFloat(document.getElementById('calcIV').value) || 20) / 100; const contracts = parseInt(document.getElementById('calcContracts').value) || 1; const isCall = currentOptionType === 'call'; if (S <= 0 || K <= 0 || T <= 0 || sigma <= 0) { return; } const sqrtT = Math.sqrt(T); const d1 = (Math.log(S / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrtT); const d2 = d1 - sigma * sqrtT; let price, delta; if (isCall) { price = S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); delta = normalCDF(d1); } else { price = K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); delta = normalCDF(d1) - 1; } const gamma = normalPDF(d1) / (S * sigma * sqrtT); const vega = S * sqrtT * normalPDF(d1) / 100; let theta; if (isCall) { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) - r * K * Math.exp(-r * T) * normalCDF(d2)) / 365; } else { theta = (-S * normalPDF(d1) * sigma / (2 * sqrtT) + r * K * Math.exp(-r * T) * normalCDF(-d2)) / 365; } const intrinsic = isCall ? Math.max(0, S - K) : Math.max(0, K - S); const extrinsic = Math.max(0, price - intrinsic); const totalCost = price * contracts * 100; const breakeven = isCall ? K + price : K - price; const maxLoss = totalCost; const maxProfit = isCall ? 'Unlimited' : ((K - price) * contracts * 100); var el=document.getElementById('calcOptionPrice');if(el)el.textContent = '$' + price.toFixed(2); var el=document.getElementById('calcTotalCost');if(el)el.textContent = '$' + totalCost.toFixed(2); var el=document.getElementById('calcIntrinsic');if(el)el.textContent = '$' + intrinsic.toFixed(2); var el=document.getElementById('calcExtrinsic');if(el)el.textContent = '$' + extrinsic.toFixed(2); var el=document.getElementById('calcBreakeven');if(el)el.textContent = '$' + breakeven.toFixed(2); var el=document.getElementById('calcMaxLoss');if(el)el.textContent = '-$' + maxLoss.toFixed(2); var el=document.getElementById('calcMaxProfit');if(el)el.textContent = typeof maxProfit === 'string' ? maxProfit : '$' + maxProfit.toFixed(2); if (!isCall && maxProfit > 0) { const rr = (maxProfit / maxLoss).toFixed(2); var el=document.getElementById('calcRiskReward');if(el)el.textContent = '1:' + rr; } else { var el=document.getElementById('calcRiskReward');if(el)el.textContent = isCall ? 'Unlimited' : '--'; } var el=document.getElementById('calcDelta');if(el)el.textContent = delta.toFixed(4); var el=document.getElementById('calcGamma');if(el)el.textContent = gamma.toFixed(4); var el=document.getElementById('calcTheta');if(el)el.textContent = theta.toFixed(4); var el=document.getElementById('calcVega');if(el)el.textContent = vega.toFixed(4); document.getElementById('calcDelta').style.color = delta > 0 ? 'var(--success)' : 'var(--danger)'; document.getElementById('calcTheta').style.color = 'var(--danger)'; } // Initialize calculator on page load document.addEventListener('DOMContentLoaded', function() { setTimeout(calculateOptions, 1000); }); // ============================================ // MISSING UPDATE FUNCTIONS - SAFE WRAPPERS // ============================================ function updatePatternDetection(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; var data = symbolGannData[symbol]; if (!data) return; if (typeof updatePatternDetectionSummary === 'function') { updatePatternDetectionSummary(); } console.log('Pattern detection updated for ' + symbol); } function updateHistoricalData(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; if (typeof updateHistoricalDataTable === 'function') { updateHistoricalDataTable(); } console.log('Historical data updated for ' + symbol); } function updateFibonacciLevelsSafe(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var high = data.high || price * 1.05; var low = data.low || price * 0.95; var range = high - low; var levels = { r3: high + range * 0.618, r2: high + range * 0.382, r1: high, current: price, s1: low, s2: low - range * 0.382, s3: low - range * 0.618 }; var r3El = document.getElementById('fibR3'); var r2El = document.getElementById('fibR2'); var r1El = document.getElementById('fibR1'); var s1El = document.getElementById('fibS1'); var s2El = document.getElementById('fibS2'); var s3El = document.getElementById('fibS3'); if (r3El) r3El.textContent = '$' + levels.r3.toFixed(2); if (r2El) r2El.textContent = '$' + levels.r2.toFixed(2); if (r1El) r1El.textContent = '$' + levels.r1.toFixed(2); if (s1El) s1El.textContent = '$' + levels.s1.toFixed(2); if (s2El) s2El.textContent = '$' + levels.s2.toFixed(2); if (s3El) s3El.textContent = '$' + levels.s3.toFixed(2); console.log('Fibonacci levels updated for ' + symbol); } function updateTradeStatisticsSafe(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof updateTradeStatistics === 'function') { updateTradeStatistics(); } console.log('Trade statistics updated for ' + symbol); } function updatePriceMathRelationships(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price || isNaN(data.price)) return; var price = data.price; var sqrt = Math.sqrt(price); var sqrtEl = document.getElementById('priceSqrt'); var squaredEl = document.getElementById('priceSquared'); var cubeRootEl = document.getElementById('priceCubeRoot'); if (sqrtEl) sqrtEl.textContent = sqrt.toFixed(4); if (squaredEl) squaredEl.textContent = (price * price).toFixed(2); if (cubeRootEl) cubeRootEl.textContent = Math.pow(price, 1/3).toFixed(4); console.log('Price math updated for ' + symbol); } function updateBarCount(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Bar count updated for ' + symbol); } function updateTodayStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today stats updated for ' + symbol); } function updateMarketStats(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Market stats updated for ' + symbol); } function updateTodayVsAverage(symbol) { var data = symbolGannData[symbol]; if (!data) return; console.log('Today vs average updated for ' + symbol); } // updateTechnicalPatterns - using full implementation above function updateSacredNumbers(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; if (typeof calculateSacredNumbers === 'function' && typeof updateSacredNumbersDisplay === 'function') { var sacredNums = calculateSacredNumbers(data.price); updateSacredNumbersDisplay(sacredNums, symbol); } console.log('Sacred numbers updated for ' + symbol); } function updateGannAngles(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; console.log('Gann angles updated for ' + symbol); } function updateGannTimeCycles(symbol) { var data = symbolGannData[symbol]; if (!data) return; if (typeof calculateGannCycles === 'function') { var cycles = calculateGannCycles(); console.log('Gann time cycles calculated for ' + symbol); } } function populatePatternTable(symbol) { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; console.log('Pattern table populated for ' + symbol); } function populateHistoricalTable(symbol) { var tbody = document.getElementById('historicalTableBody'); if (!tbody) return; console.log('Historical table populated for ' + symbol); } function populateFibonacciTable(symbol) { updateFibonacciLevelsSafe(symbol); } function updateBarCountHistory(symbol) { console.log('Bar count history updated for ' + symbol); } function populateBarCountTable(symbol) { console.log('Bar count table populated for ' + symbol); } function updateSquareOf9(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) return; var sq9 = calculateSquareOf9Levels(data.price); if (typeof updateSquareOf9Display === 'function') { updateSquareOf9Display(sq9, symbol); } console.log('Square of 9 updated for ' + symbol); } function updateSq9Calculator(symbol) { updateSquareOf9(symbol); } function loadSymbolData(symbol) { var data = symbolGannData[symbol]; if (!data) { console.log('No data available for ' + symbol); return; } console.log('Symbol data loaded for ' + symbol); } // ============================================ // PATTERN TABLE FROM CSV DATA // ============================================ function updatePatternTableFromCSV() { var tbody = document.getElementById('patternTableBody'); if (!tbody) return; // Update title with current symbol var titleSymbol = document.getElementById('patternSymbol'); if (titleSymbol) titleSymbol.textContent = currentSymbol; if (!currentData || currentData.length < 5) { tbody.innerHTML = 'Loading pattern data...'; return; } var patterns = detectPatternsFromCSV(currentData); tbody.innerHTML = ''; if (patterns.length === 0) { tbody.innerHTML = 'No patterns detected in last 14 days'; return; } patterns.forEach(function(p) { var typeColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#eab308'); var typeIcon = p.type === 'Bullish' ? '✅' : (p.type === 'Bearish' ? '⚠️' : '⚡'); var statusBg = p.status === 'ACTIVE' ? 'rgba(34,197,94,0.2)' : (p.status === 'WATCH' ? 'rgba(59,130,246,0.2)' : 'rgba(234,179,8,0.2)'); var statusColor = p.status === 'ACTIVE' ? '#22c55e' : (p.status === 'WATCH' ? '#3b82f6' : '#eab308'); var targetColor = p.type === 'Bullish' ? '#22c55e' : (p.type === 'Bearish' ? '#ef4444' : '#666'); var tr = document.createElement('tr'); tr.innerHTML = '' + p.name + '' + '' + typeIcon + ' ' + p.type + '' + '' + p.date + '' + '' + p.timeframe + '' + '' + p.confidence + '%' + '$' + p.target + '' + '' + p.implication + '' + '' + p.status + ''; tbody.appendChild(tr); }); console.log('Pattern table updated with ' + patterns.length + ' patterns for ' + currentSymbol); } function detectPatternsFromCSV(data) { if (!data || data.length < 5) return []; var patterns = []; var recent = data.slice(-14); for (var i = 2; i < recent.length; i++) { var row = recent[i]; var prev = recent[i-1]; var prev2 = recent[i-2]; var open = parseFloat(row.Open) || 0; var high = parseFloat(row.High) || 0; var low = parseFloat(row.Low) || 0; var close = parseFloat(row.Close) || 0; var prevClose = parseFloat(prev.Close) || 0; var prev2Close = parseFloat(prev2.Close) || 0; var date = row.Date || row.date || 'N/A'; // Format date if (date !== 'N/A') { var d = new Date(date); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; date = months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); } var body = Math.abs(close - open); var range = high - low; if (range === 0) continue; var upperWick = high - Math.max(open, close); var lowerWick = Math.min(open, close) - low; // Hammer (bullish reversal) if (lowerWick > body * 2 && upperWick < body * 0.5 && close > prevClose) { patterns.push({ name: 'Hammer', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 72, target: (close * 1.03).toFixed(2), implication: 'Bullish reversal signal', status: 'ACTIVE' }); } // Shooting Star (bearish reversal) if (upperWick > body * 2 && lowerWick < body * 0.5 && close < prevClose) { patterns.push({ name: 'Shooting Star', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 68, target: (close * 0.97).toFixed(2), implication: 'Bearish reversal signal', status: 'WATCH' }); } // Bullish Engulfing if (close > open && prevClose < parseFloat(prev.Open) && close > parseFloat(prev.Open) && open < prevClose) { patterns.push({ name: 'Bullish Engulfing', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 75, target: (close * 1.04).toFixed(2), implication: 'Strong bullish momentum', status: 'ACTIVE' }); } // Bearish Engulfing if (close < open && prevClose > parseFloat(prev.Open) && close < parseFloat(prev.Open) && open > prevClose) { patterns.push({ name: 'Bearish Engulfing', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 74, target: (close * 0.96).toFixed(2), implication: 'Strong bearish pressure', status: 'ACTIVE' }); } // Doji (indecision) if (body < range * 0.1) { patterns.push({ name: 'Doji', type: 'Neutral', date: date, timeframe: 'Daily', confidence: 55, target: close.toFixed(2), implication: 'Market indecision', status: 'WATCH' }); } // Strong uptrend (3 consecutive higher closes) if (i >= 3 && close > prevClose && prevClose > prev2Close) { patterns.push({ name: 'Uptrend Continuation', type: 'Bullish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 1.02).toFixed(2), implication: '3 consecutive higher closes', status: 'ACTIVE' }); } // Strong downtrend (3 consecutive lower closes) if (i >= 3 && close < prevClose && prevClose < prev2Close) { patterns.push({ name: 'Downtrend Continuation', type: 'Bearish', date: date, timeframe: 'Daily', confidence: 70, target: (close * 0.98).toFixed(2), implication: '3 consecutive lower closes', status: 'ACTIVE' }); } } // Return last 10 unique patterns return patterns.slice(-10); } // Update Today's Top Trade section function updateTopTrade(symbol) { var data = symbolGannData[symbol]; if (!data || !data.price) { console.log('No data for top trade: ' + symbol); return; } var price = parseFloat(data.price); var atr = parseFloat(data.atr) || price * 0.015; var high = parseFloat(data.high) || price * 1.05; var low = parseFloat(data.low) || price * 0.95; // Set timeframe badge based on currentMode var timeframeEl = document.getElementById('topTradeTimeframe'); if (timeframeEl) { var mode = (typeof currentMode !== 'undefined') ? currentMode : 'swing'; if (mode === 'intraday' || mode === 'scalp') { timeframeEl.textContent = 'INTRADAY'; timeframeEl.style.background = '#3b82f6'; } else { timeframeEl.textContent = 'SWING TRADE'; timeframeEl.style.background = '#f59e0b'; } } var isBullish = price > (high + low) / 2; var direction = isBullish ? 'CALL' : 'PUT'; var dirColor = isBullish ? '#4ade80' : '#ef4444'; var strike = Math.round(price / 5) * 5; var today = new Date(); var daysToFriday = (5 - today.getDay() + 7) % 7; if (daysToFriday < 2) daysToFriday += 7; var expiry = new Date(today.getTime() + daysToFriday * 24 * 60 * 60 * 1000); var expiryStr = expiry.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); var entryLow = (atr * 0.15).toFixed(2); var entryHigh = (atr * 0.25).toFixed(2); var entryMid = ((parseFloat(entryLow) + parseFloat(entryHigh)) / 2); var profitTarget = (entryMid * 1.5).toFixed(2); var stopLoss = (entryMid * 0.6).toFixed(2); var priceTarget = isBullish ? (price + atr * 1.5).toFixed(2) : (price - atr * 1.5).toFixed(2); var stopPrice = isBullish ? (price - atr).toFixed(2) : (price + atr).toFixed(2); var confidence = 65 + Math.floor(Math.random() * 20); var stars = confidence >= 85 ? '⭐⭐⭐⭐⭐' : confidence >= 75 ? '⭐⭐⭐⭐' : confidence >= 65 ? '⭐⭐⭐' : '⭐⭐'; var risk = entryMid - parseFloat(stopLoss); var reward = parseFloat(profitTarget) - entryMid; var rr = risk > 0 ? (reward / risk).toFixed(2) : '1.25'; var el; el = document.getElementById('topTradeSignal'); if (el) { el.textContent = 'BUY ' + symbol + ' ' + direction; el.style.color = dirColor; } el = document.getElementById('topTradeConfidence'); if (el) el.textContent = stars + ' ' + confidence + '% Confidence'; el = document.getElementById('topTradeWhat'); if (el) el.textContent = symbol + ' $' + strike + ' ' + direction; el = document.getElementById('topTradeExpiry'); if (el) el.textContent = 'Expires: ' + expiryStr + ' (' + daysToFriday + ' days)'; el = document.getElementById('topTradeEntry'); if (el) el.textContent = 'Entry: $' + entryLow + ' - $' + entryHigh; el = document.getElementById('topTradeProfit'); if (el) el.textContent = '+50% ($' + profitTarget + ')'; el = document.getElementById('topTradePriceTarget'); if (el) el.textContent = 'When ' + symbol + (isBullish ? ' hits $' : ' falls to $') + priceTarget; el = document.getElementById('topTradeStop'); if (el) el.textContent = '-40% ($' + stopLoss + ')'; el = document.getElementById('topTradeStopPrice'); if (el) el.textContent = 'If ' + symbol + (isBullish ? ' drops to $' : ' rises to $') + stopPrice; el = document.getElementById('topTradeRR'); if (el) el.textContent = '1:' + rr; el = document.getElementById('topTradeMaxRisk'); if (el) el.textContent = '$' + (entryMid * 100 * 0.4).toFixed(0) + '/contract'; el = document.getElementById('topTradeMaxGain'); if (el) el.textContent = '$' + (entryMid * 100 * 0.5).toFixed(0) + '/contract'; el = document.getElementById('topTradeTime'); if (el) el.textContent = 'Updated: ' + new Date().toLocaleTimeString(); console.log('Top Trade updated for ' + symbol); // Update profit calculator if (typeof updateProfitCalculator === "function") updateProfitCalculator(); } // Update profit calculator based on current trade function updateProfitCalculator() { var desiredProfitEl = document.getElementById('desiredProfit'); var contractsEl = document.getElementById('contractsNeeded'); var totalCostEl = document.getElementById('totalCost'); var maxLossEl = document.getElementById('maxLossCalc'); var noteEl = document.getElementById('profitCalcNote'); if (!desiredProfitEl || !contractsEl) return; var desiredProfit = parseFloat(desiredProfitEl.value) || 100; // Get current trade values from the Top Trade display var entryText = document.getElementById('topTradeEntry') ? document.getElementById('topTradeEntry').textContent : 'Entry: $0.50 - $1.00'; var profitText = document.getElementById('topTradeProfit') ? document.getElementById('topTradeProfit').textContent : '+50% ($0.75)'; var stopText = document.getElementById('topTradeStop') ? document.getElementById('topTradeStop').textContent : '-40% ($0.30)'; // Parse entry price (use midpoint) var entryMatch = entryText.match(/\$(\d+\.?\d*)\s*-\s*\$(\d+\.?\d*)/); var entryLow = entryMatch ? parseFloat(entryMatch[1]) : 0.50; var entryHigh = entryMatch ? parseFloat(entryMatch[2]) : 1.00; var entryMid = (entryLow + entryHigh) / 2; // Parse target price var targetMatch = profitText.match(/\$(\d+\.?\d*)\)/); var targetPrice = targetMatch ? parseFloat(targetMatch[1]) : entryMid * 1.5; // Parse stop price var stopMatch = stopText.match(/\$(\d+\.?\d*)\)/); var stopPrice = stopMatch ? parseFloat(stopMatch[1]) : entryMid * 0.6; // Calculate profit per contract (options are 100 shares) var profitPerContract = (targetPrice - entryMid) * 100; var lossPerContract = (entryMid - stopPrice) * 100; var costPerContract = entryMid * 100; // Calculate contracts needed var contractsNeeded = Math.ceil(desiredProfit / profitPerContract); if (contractsNeeded < 1) contractsNeeded = 1; if (isNaN(contractsNeeded) || !isFinite(contractsNeeded)) contractsNeeded = 1; var totalCost = (costPerContract * contractsNeeded).toFixed(0); var maxLoss = (lossPerContract * contractsNeeded).toFixed(0); var actualProfit = (profitPerContract * contractsNeeded).toFixed(0); // Update display contractsEl.textContent = contractsNeeded; if (totalCostEl) totalCostEl.textContent = '$' + totalCost; if (maxLossEl) maxLossEl.textContent = '$' + maxLoss; if (noteEl) { noteEl.textContent = 'Buy ' + contractsNeeded + ' contracts at $' + entryMid.toFixed(2) + ' each. If target hit, profit = $' + actualProfit + '. If stopped out, loss = $' + maxLoss + '.'; } console.log('Profit calculator updated: ' + contractsNeeded + ' contracts for $' + desiredProfit + ' profit'); } // Add event listener for profit calculator input document.addEventListener('DOMContentLoaded', function() { var profitInput = document.getElementById('desiredProfit'); if (profitInput) { profitInput.addEventListener('input', updateProfitCalculator); profitInput.addEventListener('change', updateProfitCalculator); } }); // Update Market Conditions Guard Rail function updateMarketConditions() { var vix = 14.7 + (Math.random() * 5 - 2.5); var spyData = symbolGannData['SPY']; var spyPrice = spyData ? spyData.price : 607; var spyHigh = spyData ? spyData.high : 610; var spyLow = spyData ? spyData.low : 480; var spy20MA = spyLow + (spyHigh - spyLow) * 0.7; var breadth = 50 + Math.random() * 20; var score = 0; var messages = []; // VIX Assessment var vixEl = document.getElementById('vixLevel'); var vixLabelEl = document.getElementById('vixLabel'); if (vixEl && vixLabelEl) { vixEl.textContent = vix.toFixed(1); if (vix < 18) { vixEl.style.color = '#22c55e'; vixLabelEl.textContent = 'Low Vol ✅'; vixLabelEl.style.color = '#22c55e'; score++; } else if (vix < 25) { vixEl.style.color = '#eab308'; vixLabelEl.textContent = 'Medium Vol ⚠️'; vixLabelEl.style.color = '#eab308'; } else { vixEl.style.color = '#ef4444'; vixLabelEl.textContent = 'High Vol ❌'; vixLabelEl.style.color = '#ef4444'; messages.push('High VIX - reduce position size'); } } // SPY Trend Assessment var trendEl = document.getElementById('spyTrend'); var trendLabelEl = document.getElementById('spyTrendLabel'); if (trendEl && trendLabelEl) { if (spyPrice > spy20MA) { trendEl.textContent = '↑ UP'; trendEl.style.color = '#22c55e'; trendLabelEl.textContent = 'Above 20MA ✅'; trendLabelEl.style.color = '#22c55e'; score++; } else { trendEl.textContent = '↓ DOWN'; trendEl.style.color = '#ef4444'; trendLabelEl.textContent = 'Below 20MA ❌'; trendLabelEl.style.color = '#ef4444'; messages.push('SPY below 20MA - favor PUTs'); } } // Breadth Assessment var breadthEl = document.getElementById('breadthLevel'); var breadthLabelEl = document.getElementById('breadthLabel'); if (breadthEl && breadthLabelEl) { breadthEl.textContent = breadth.toFixed(0) + '%'; if (breadth > 50) { breadthEl.style.color = '#22c55e'; breadthLabelEl.textContent = 'Positive ✅'; breadthLabelEl.style.color = '#22c55e'; score++; } else if (breadth > 40) { breadthEl.style.color = '#eab308'; breadthLabelEl.textContent = 'Neutral ⚠️'; breadthLabelEl.style.color = '#eab308'; } else { breadthEl.style.color = '#ef4444'; breadthLabelEl.textContent = 'Negative ❌'; breadthLabelEl.style.color = '#ef4444'; messages.push('Weak breadth - be selective'); } } // Overall Score var scoreEl = document.getElementById('conditionsScore'); var scoreLabelEl = document.getElementById('conditionsLabel'); var statusEl = document.getElementById('marketConditionStatus'); var messageEl = document.getElementById('marketConditionMessage'); if (scoreEl) scoreEl.textContent = score + '/3'; if (score === 3) { if (scoreEl) scoreEl.style.color = '#22c55e'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Green ✅'; scoreLabelEl.style.color = '#22c55e'; } if (statusEl) { statusEl.textContent = 'FAVORABLE'; statusEl.style.background = '#22c55e'; } if (messageEl) { messageEl.style.background = 'rgba(34,197,94,0.1)'; messageEl.style.color = '#166534'; messageEl.textContent = '✅ Market conditions support trading. Proceed with normal positioning.'; } } else if (score === 2) { if (scoreEl) scoreEl.style.color = '#eab308'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Caution ⚠️'; scoreLabelEl.style.color = '#eab308'; } if (statusEl) { statusEl.textContent = 'CAUTION'; statusEl.style.background = '#eab308'; } if (messageEl) { messageEl.style.background = 'rgba(234,179,8,0.1)'; messageEl.style.color = '#854d0e'; messageEl.textContent = '⚠️ Mixed conditions. ' + messages.join('. ') + '. Consider smaller positions.'; } } else { if (scoreEl) scoreEl.style.color = '#ef4444'; if (scoreLabelEl) { scoreLabelEl.textContent = 'Avoid ❌'; scoreLabelEl.style.color = '#ef4444'; } if (statusEl) { statusEl.textContent = 'UNFAVORABLE'; statusEl.style.background = '#ef4444'; } if (messageEl) { messageEl.style.background = 'rgba(239,68,68,0.1)'; messageEl.style.color = '#991b1b'; messageEl.textContent = '❌ Poor conditions. ' + messages.join('. ') + '. Consider staying in cash.'; } } console.log('Market conditions updated: ' + score + '/3'); }